Skip to contentSkip to conversation

wifi: ath11k: hibernation support

RFC PATCH In review Kalle Valo 5 participants 34 messages Nov 27, 2023 – Feb 2, 2024

Subsystems (2)
lore
Share & tools
lore

Kalle Valo

author
#0 lore raw

v2 cover wifi: ath11k: hibernation support

From: Kalle Valo <[email protected]>

Currently in ath11k we keep the firmware running on the WLAN device when the network interface (wlan0) is down. The problem is that this will break hibernation, obviously the firmware can't be running after the whole system is powered off. To power down the ath11k firmware for suspend/hibernation some changes both in MHI subsystem and ath11k is needed.

This patchset fixes a longstanding bug report about broken hibernation support:

https://bugzilla.kernel.org/show_bug.cgi?id=214649

This patchset is marked as RFC as it requires changes in MHI subsystem. Also this has been tested only on WCN6855, need to test also on more AP based chipsets like IPQ8074 and QCN9074.

The patches are also available at:

https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/log/?h=ath11k-hibernation-support

Earlier versions of this patchset have been tested by multiple users with positive results.

v2:

* rebase to ath-202311221826 (6.7.0-rc2-wt-ath+)

* 'bus: mhi: host: add mhi_power_down_no_destroy()': fix null state string for DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': fix typos in comments

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': honour initial autoqueue configuration

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': don't prepare/unprepare MHI devices that don't match with a MHI client driver

* 'wifi: ath11k: remove MHI LOOPBACK channels': remove LOOPBACK channels for QCN9074 as well

v1: https://lore.kernel.org/mhi/[email protected]/

Baochen Qiang (7):
bus: mhi: host: add mhi_power_down_no_destroy()
bus: mhi: host: add new interfaces to handle MHI channels directly
wifi: ath11k: handle irq enable/disable in several code path
wifi: ath11k: remove MHI LOOPBACK channels
wifi: ath11k: do not dump SRNG statistics during resume
wifi: ath11k: fix warning on DMA ring capabilities event
wifi: ath11k: support hibernation

Kalle Valo (1):
wifi: ath11k: thermal: don't try to register multiple times

 drivers/bus/mhi/host/init.c               |   1 +
 drivers/bus/mhi/host/internal.h           |   1 +
 drivers/bus/mhi/host/main.c               | 107 ++++++++++++++++++++++
 drivers/bus/mhi/host/pm.c                 |  26 ++++--
 drivers/net/wireless/ath/ath11k/ahb.c     |   8 +-
 drivers/net/wireless/ath/ath11k/core.c    |  44 +++++----
 drivers/net/wireless/ath/ath11k/core.h    |   2 +
 drivers/net/wireless/ath/ath11k/hif.h     |  12 +--
 drivers/net/wireless/ath/ath11k/mhi.c     |  77 ++++------------
 drivers/net/wireless/ath/ath11k/mhi.h     |   4 +-
 drivers/net/wireless/ath/ath11k/pci.c     |  55 +++++++++--
 drivers/net/wireless/ath/ath11k/qmi.c     |   7 +-
 drivers/net/wireless/ath/ath11k/thermal.c |   3 +
 drivers/net/wireless/ath/ath11k/wmi.c     |   1 +
 include/linux/mhi.h                       |  47 +++++++++-
 15 files changed, 285 insertions(+), 110 deletions(-)

base-commit: 16a212b4f33c4edd9ce9a9e0953b5389216e8ed9

Signature · 1 line
2.39.2
author
#1 lore raw

RFC PATCH v2 1/8 bus: mhi: host: add mhi_power_down_no_destroy()

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/bus/mhi/host/init.c     |  1 +
 drivers/bus/mhi/host/internal.h |  1 +
 drivers/bus/mhi/host/pm.c       | 26 +++++++++++++++++++-------
 include/linux/mhi.h             | 29 +++++++++++++++++++++++++++--
 4 files changed, 48 insertions(+), 9 deletions(-)
drivers/bus/mhi/host/init.c +1 -0
index 65ceac1837f9..e626b03ffafa 100644
@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
 	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
 	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
 	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+	[DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
 };
 
 const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
drivers/bus/mhi/host/internal.h +1 -0
index 30ac415a3000..3f45c9c447bd 100644
@@ -69,6 +69,7 @@ enum dev_st_transition {
 	DEV_ST_TRANSITION_FP,
 	DEV_ST_TRANSITION_SYS_ERR,
 	DEV_ST_TRANSITION_DISABLE,
+	DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
 	DEV_ST_TRANSITION_MAX,
 };
 
drivers/bus/mhi/host/pm.c +19 -7
index a2f2feef1476..8833b0248393 100644
@@ -458,7 +458,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
 }
 
 /* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+				      bool destroy_device)
 {
 	enum mhi_pm_state cur_state;
 	struct mhi_event *mhi_event;
@@ -520,8 +521,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
 	dev_dbg(dev, "Waiting for all pending threads to complete\n");
 	wake_up_all(&mhi_cntrl->state_event);
 
-	dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	if (destroy_device) {
+		dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+		device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	}
 
 	mutex_lock(&mhi_cntrl->pm_mutex);
 
@@ -806,7 +809,10 @@ void mhi_pm_st_worker(struct work_struct *work)
 			mhi_pm_sys_error_transition(mhi_cntrl);
 			break;
 		case DEV_ST_TRANSITION_DISABLE:
-			mhi_pm_disable_transition(mhi_cntrl);
+			mhi_pm_disable_transition(mhi_cntrl, false);
+			break;
+		case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+			mhi_pm_disable_transition(mhi_cntrl, true);
 			break;
 		default:
 			break;
@@ -1160,7 +1166,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
 }
 EXPORT_SYMBOL_GPL(mhi_async_power_up);
 
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		      bool destroy_device)
 {
 	enum mhi_pm_state cur_state, transition_state;
 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1196,14 +1203,19 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
 	write_unlock_irq(&mhi_cntrl->pm_lock);
 	mutex_unlock(&mhi_cntrl->pm_mutex);
 
-	mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+	if (destroy_device)
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+	else
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE);
 
 	/* Wait for shutdown to complete */
 	flush_work(&mhi_cntrl->st_worker);
 
 	disable_irq(mhi_cntrl->irq[0]);
 }
-EXPORT_SYMBOL_GPL(mhi_power_down);
+EXPORT_SYMBOL_GPL(__mhi_power_down);
 
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
 {
include/linux/mhi.h +27 -2
index d0f9b522f328..ae092bc8b97e 100644
@@ -648,12 +648,37 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
  */
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
 
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		    bool destroy_device);
+
 /**
- * mhi_power_down - Start MHI power down sequence
+ * mhi_power_down - Start MHI power down sequence. See also
+ * mhi_power_down_no_destroy() which is a variant of this for suspend.
+ *
  * @mhi_cntrl: MHI controller
  * @graceful: Link is still accessible, so do a graceful shutdown process
  */
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, true);
+}
+
+/**
+ * mhi_power_down_no_destroy - Start MHI power down sequence but don't
+ * destroy struct devices. This is a variant for mhi_power_down() and is a
+ * workaround to make it possible to use mhi_power_up() in a resume
+ * handler. When using this variant the caller must also call
+ * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_unprepare_all_from_transfer().
+ *
+ * @mhi_cntrl: MHI controller
+ * @graceful: Link is still accessible, so do a graceful shutdown process
+ */
+static inline void mhi_power_down_no_destroy(struct mhi_controller *mhi_cntrl,
+					     bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, false);
+}
 
 /**
  * mhi_unprepare_after_power_down - Free any allocated memory after power down
Signature · 1 line
2.39.2
#2 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 37 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

Quoted 8 lines

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank? As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed. We should try to explore alternate options instead of persisting with this solution.

- Mani

Quoted 150 lines
---
 drivers/bus/mhi/host/init.c     |  1 +
 drivers/bus/mhi/host/internal.h |  1 +
 drivers/bus/mhi/host/pm.c       | 26 +++++++++++++++++++-------
 include/linux/mhi.h             | 29 +++++++++++++++++++++++++++--
 4 files changed, 48 insertions(+), 9 deletions(-)
drivers/bus/mhi/host/init.c +1 -0
index 65ceac1837f9..e626b03ffafa 100644
@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
 	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
 	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
 	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+	[DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
 };
 
 const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
drivers/bus/mhi/host/internal.h +1 -0
index 30ac415a3000..3f45c9c447bd 100644
@@ -69,6 +69,7 @@ enum dev_st_transition {
 	DEV_ST_TRANSITION_FP,
 	DEV_ST_TRANSITION_SYS_ERR,
 	DEV_ST_TRANSITION_DISABLE,
+	DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
 	DEV_ST_TRANSITION_MAX,
 };
 
drivers/bus/mhi/host/pm.c +19 -7
index a2f2feef1476..8833b0248393 100644
@@ -458,7 +458,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
 }
 
 /* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+				      bool destroy_device)
 {
 	enum mhi_pm_state cur_state;
 	struct mhi_event *mhi_event;
@@ -520,8 +521,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
 	dev_dbg(dev, "Waiting for all pending threads to complete\n");
 	wake_up_all(&mhi_cntrl->state_event);
 
-	dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	if (destroy_device) {
+		dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+		device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	}
 
 	mutex_lock(&mhi_cntrl->pm_mutex);
 
@@ -806,7 +809,10 @@ void mhi_pm_st_worker(struct work_struct *work)
 			mhi_pm_sys_error_transition(mhi_cntrl);
 			break;
 		case DEV_ST_TRANSITION_DISABLE:
-			mhi_pm_disable_transition(mhi_cntrl);
+			mhi_pm_disable_transition(mhi_cntrl, false);
+			break;
+		case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+			mhi_pm_disable_transition(mhi_cntrl, true);
 			break;
 		default:
 			break;
@@ -1160,7 +1166,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
 }
 EXPORT_SYMBOL_GPL(mhi_async_power_up);
 
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		      bool destroy_device)
 {
 	enum mhi_pm_state cur_state, transition_state;
 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1196,14 +1203,19 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
 	write_unlock_irq(&mhi_cntrl->pm_lock);
 	mutex_unlock(&mhi_cntrl->pm_mutex);
 
-	mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+	if (destroy_device)
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+	else
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE);
 
 	/* Wait for shutdown to complete */
 	flush_work(&mhi_cntrl->st_worker);
 
 	disable_irq(mhi_cntrl->irq[0]);
 }
-EXPORT_SYMBOL_GPL(mhi_power_down);
+EXPORT_SYMBOL_GPL(__mhi_power_down);
 
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
 {
include/linux/mhi.h +27 -2
index d0f9b522f328..ae092bc8b97e 100644
@@ -648,12 +648,37 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
  */
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
 
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		    bool destroy_device);
+
 /**
- * mhi_power_down - Start MHI power down sequence
+ * mhi_power_down - Start MHI power down sequence. See also
+ * mhi_power_down_no_destroy() which is a variant of this for suspend.
+ *
  * @mhi_cntrl: MHI controller
  * @graceful: Link is still accessible, so do a graceful shutdown process
  */
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, true);
+}
+
+/**
+ * mhi_power_down_no_destroy - Start MHI power down sequence but don't
+ * destroy struct devices. This is a variant for mhi_power_down() and is a
+ * workaround to make it possible to use mhi_power_up() in a resume
+ * handler. When using this variant the caller must also call
+ * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_unprepare_all_from_transfer().
+ *
+ * @mhi_cntrl: MHI controller
+ * @graceful: Link is still accessible, so do a graceful shutdown process
+ */
+static inline void mhi_power_down_no_destroy(struct mhi_controller *mhi_cntrl,
+					     bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, false);
+}
 
 /**
  * mhi_unprepare_after_power_down - Free any allocated memory after power down
Signature · 1 line
2.39.2
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#3 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 47 lines · Manivannan Sadhasivam
Quoted 37 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

Quoted 8 lines

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Quoted 7 lines

Any reason for reposting this series without discussing the suggestion from Mayank? As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed. We should try to explore alternate options instead of persisting with this solution.

- Mani

I shared my opinion on Mayank's proposal, but not noticed it is in internal thead. I will repost here:

Mayank's proposal assumes that at the time PM_POST_HIBERNATION is posted, all deferred probe is unblocked and completed, however which is not true.

Yes, kernel's sequence is first unblock probe defer and trigger probe again, then post PM_POST_HIBERNATION message. But the problem here is that the probe-trigger is delegated to an work item and returns immediately, kernel does not wait for all probe to complete before posting PM_POST_HIBERNATION. So it is not guaranteed that an MHI device is probed at the time we get that message.

Quoted 152 lines
Quoted 151 lines
---
  drivers/bus/mhi/host/init.c     |  1 +
  drivers/bus/mhi/host/internal.h |  1 +
  drivers/bus/mhi/host/pm.c       | 26 +++++++++++++++++++-------
  include/linux/mhi.h             | 29 +++++++++++++++++++++++++++--
  4 files changed, 48 insertions(+), 9 deletions(-)
drivers/bus/mhi/host/init.c +1 -0
index 65ceac1837f9..e626b03ffafa 100644
@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
  	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
  	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
  	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+	[DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
  };
 
  const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
drivers/bus/mhi/host/internal.h +1 -0
index 30ac415a3000..3f45c9c447bd 100644
@@ -69,6 +69,7 @@ enum dev_st_transition {
  	DEV_ST_TRANSITION_FP,
  	DEV_ST_TRANSITION_SYS_ERR,
  	DEV_ST_TRANSITION_DISABLE,
+	DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
  	DEV_ST_TRANSITION_MAX,
  };
 
drivers/bus/mhi/host/pm.c +19 -7
index a2f2feef1476..8833b0248393 100644
@@ -458,7 +458,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
  }
 
  /* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+				      bool destroy_device)
  {
  	enum mhi_pm_state cur_state;
  	struct mhi_event *mhi_event;
@@ -520,8 +521,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
  	dev_dbg(dev, "Waiting for all pending threads to complete\n");
  	wake_up_all(&mhi_cntrl->state_event);
 
-	dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	if (destroy_device) {
+		dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+		device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	}
 
  	mutex_lock(&mhi_cntrl->pm_mutex);
 
@@ -806,7 +809,10 @@ void mhi_pm_st_worker(struct work_struct *work)
  			mhi_pm_sys_error_transition(mhi_cntrl);
  			break;
  		case DEV_ST_TRANSITION_DISABLE:
-			mhi_pm_disable_transition(mhi_cntrl);
+			mhi_pm_disable_transition(mhi_cntrl, false);
+			break;
+		case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+			mhi_pm_disable_transition(mhi_cntrl, true);
  			break;
  		default:
  			break;
@@ -1160,7 +1166,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
  }
  EXPORT_SYMBOL_GPL(mhi_async_power_up);
 
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		      bool destroy_device)
  {
  	enum mhi_pm_state cur_state, transition_state;
  	struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1196,14 +1203,19 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
  	write_unlock_irq(&mhi_cntrl->pm_lock);
  	mutex_unlock(&mhi_cntrl->pm_mutex);
 
-	mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+	if (destroy_device)
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+	else
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE);
 
  	/* Wait for shutdown to complete */
  	flush_work(&mhi_cntrl->st_worker);
 
  	disable_irq(mhi_cntrl->irq[0]);
  }
-EXPORT_SYMBOL_GPL(mhi_power_down);
+EXPORT_SYMBOL_GPL(__mhi_power_down);
 
  int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
  {
include/linux/mhi.h +27 -2
index d0f9b522f328..ae092bc8b97e 100644
@@ -648,12 +648,37 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
   */
  int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
 
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		    bool destroy_device);
+
  /**
- * mhi_power_down - Start MHI power down sequence
+ * mhi_power_down - Start MHI power down sequence. See also
+ * mhi_power_down_no_destroy() which is a variant of this for suspend.
+ *
   * @mhi_cntrl: MHI controller
   * @graceful: Link is still accessible, so do a graceful shutdown process
   */
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, true);
+}
+
+/**
+ * mhi_power_down_no_destroy - Start MHI power down sequence but don't
+ * destroy struct devices. This is a variant for mhi_power_down() and is a
+ * workaround to make it possible to use mhi_power_up() in a resume
+ * handler. When using this variant the caller must also call
+ * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_unprepare_all_from_transfer().
+ *
+ * @mhi_cntrl: MHI controller
+ * @graceful: Link is still accessible, so do a graceful shutdown process
+ */
+static inline void mhi_power_down_no_destroy(struct mhi_controller *mhi_cntrl,
+					     bool graceful)
+{
+	__mhi_power_down(mhi_cntrl, graceful, false);
+}
 
  /**
   * mhi_unprepare_after_power_down - Free any allocated memory after power down
Signature · 1 line
2.39.2
author
#4 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

In reply to Manivannan Sadhasivam
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
#5 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 69 lines · Kalle Valo
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Quoted 8 lines
Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Adding Mayank directly plus others to this discussion since we need a solution to have proper hibernation support for devices containing ath11k (and in the near future ath12k).

/jeff
/jeff

#6 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

In reply to Kalle Valo
Quoted 62 lines · Kalle Valo
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

Quoted 7 lines

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Devices (struct dev) for each channels are created once the device (WLAN) enters runtime mode such as (MISSION, SBL etc...). During hibernation, ath11k stack calls mhi_power_down() which essentially resets the device to POR and also the stack powers down the device properly.

In that case, MHI channels do not exist as the device (WLAN) itself is powered down. As per kernel driver model, each struct device is tied to its reference count. And the reference count should be decremented whenever the actual device is not in use. Once the actual device is removed from the system, then the respective struct device has to be destroyed altogether.

So in this case, even though the channels are not active (present) in the device, the device itself gets powered off, you want MHI stack to keep the struct device active, which is against the model I referenced above.

To fix this issue properly, we need to investigate on how other subsystems are handling this situation (device getting powered down during hibernation), like USB.

- Mani

Quoted 12 lines
Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#7 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 82 lines · Manivannan Sadhasivam
Quoted 62 lines · Kalle Valo
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

Quoted 7 lines

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Devices (struct dev) for each channels are created once the device (WLAN) enters runtime mode such as (MISSION, SBL etc...). During hibernation, ath11k stack calls mhi_power_down() which essentially resets the device to POR and also the stack powers down the device properly.

In that case, MHI channels do not exist as the device (WLAN) itself is powered down. As per kernel driver model, each struct device is tied to its reference count. And the reference count should be decremented whenever the actual device is not in use. Once the actual device is removed from the system, then the respective struct device has to be destroyed altogether.

Quoted 8 lines

So in this case, even though the channels are not active (present) in the device, the device itself gets powered off, you want MHI stack to keep the struct device active, which is against the model I referenced above.

To fix this issue properly, we need to investigate on how other subsystems are handling this situation (device getting powered down during hibernation), like USB.

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

- Mani

Quoted 17 lines

- Mani

Quoted 12 lines
Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Signature · 1 line
மணிவண்ணன் சதாசிவம்
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#8 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 92 lines · Manivannan Sadhasivam
Quoted 82 lines · Manivannan Sadhasivam
Quoted 62 lines · Kalle Valo
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

Quoted 7 lines

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Devices (struct dev) for each channels are created once the device (WLAN) enters runtime mode such as (MISSION, SBL etc...). During hibernation, ath11k stack calls mhi_power_down() which essentially resets the device to POR and also the stack powers down the device properly.

In that case, MHI channels do not exist as the device (WLAN) itself is powered down. As per kernel driver model, each struct device is tied to its reference count. And the reference count should be decremented whenever the actual device is not in use. Once the actual device is removed from the system, then the respective struct device has to be destroyed altogether.

Quoted 8 lines

So in this case, even though the channels are not active (present) in the device, the device itself gets powered off, you want MHI stack to keep the struct device active, which is against the model I referenced above.

To fix this issue properly, we need to investigate on how other subsystems are handling this situation (device getting powered down during hibernation), like USB.

Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Quoted 21 lines

- Mani

Quoted 17 lines

- Mani

Quoted 12 lines
Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#9 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 100 lines · Baochen Qiang
Quoted 97 lines · Manivannan Sadhasivam
Quoted 82 lines · Manivannan Sadhasivam
Quoted 62 lines · Kalle Valo
Quoted 43 lines · Manivannan Sadhasivam
Quoted 41 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Quoted 7 lines
Quoted 4 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Any reason for reposting this series without discussing the suggestion from Mayank?

Baochen quickly sent me fixes for the v1 review comments, as I have been out of office for some time I didn't want to sit on Baochen's fixes for too long. Better to get them out of the door as soon as possible. I will definitely look at Mayank's proposal but that will take longer.

Quoted 3 lines

As I said in the internal thread, this patch breaks the Linux device driver model by not destroying the "struct device" when the actual device gets removed.

Quoted 7 lines

This patchset has been tested by several people, I'm even using this patchset on main laptop every day, and we haven't noticed any issues.

Can you elaborate more about this driver model? We are not removing any ath11k devices, we just want to power down the ath11k (and in the future ath12k) devices for suspend and power up during resume.

Devices (struct dev) for each channels are created once the device (WLAN) enters runtime mode such as (MISSION, SBL etc...). During hibernation, ath11k stack calls mhi_power_down() which essentially resets the device to POR and also the stack powers down the device properly.

In that case, MHI channels do not exist as the device (WLAN) itself is powered down. As per kernel driver model, each struct device is tied to its reference count. And the reference count should be decremented whenever the actual device is not in use. Once the actual device is removed from the system, then the respective struct device has to be destroyed altogether.

Quoted 8 lines

So in this case, even though the channels are not active (present) in the device, the device itself gets powered off, you want MHI stack to keep the struct device active, which is against the model I referenced above.

To fix this issue properly, we need to investigate on how other subsystems are handling this situation (device getting powered down during hibernation), like USB.

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

Quoted 7 lines
Quoted 2 lines

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

- Mani

Quoted 22 lines
Quoted 21 lines

- Mani

Quoted 17 lines

- Mani

Quoted 12 lines
Quoted 2 lines

We should try to explore alternate options instead of persisting with this solution.

What other options we have here? At least Baochen is not optimistic that using PM_POST_HIBERNATION as a workaround would work. The issue we have here is that mhi_power_up() doesn't work in the resume handler and that's what we should try to fix, not make workarounds.

Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Signature · 1 line
மணிவண்ணன் சதாசிவம்
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#10 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

On Thu, Jan 04, 2024 at 11:39:12AM +0530, Manivannan Sadhasivam wrote:

+ Can, Qiang

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

Quoted 9 lines
Quoted 5 lines

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

We reproduced the issue with the help of PCIe team (thanks Can). What we found out was, during the resume from hibernation the faliure happens in ath11k_core_resume(). Precisely here:
https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/tree/drivers/net/wireless/ath/ath11k/core.c?h=ath11k-hibernation-support#n850

This code waits for the QMI messages to arrive and eventually timesout. But the impression I got from the start was that the mhi_power_up() always fails during resume. In our investigation, we confirmed that the failure is not happening at the MHI level.

I'm not pointing fingers here, but trying to understand why can't you fix ath11k_core_resume() to not timeout? IMO this timeout should be handled as a deferral case.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#11 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 28 lines · Manivannan Sadhasivam

On Thu, Jan 04, 2024 at 11:39:12AM +0530, Manivannan Sadhasivam wrote:

+ Can, Qiang

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

Quoted 9 lines
Quoted 5 lines

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

We reproduced the issue with the help of PCIe team (thanks Can). What we found out was, during the resume from hibernation the faliure happens in ath11k_core_resume(). Precisely here:
https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/tree/drivers/net/wireless/ath/ath11k/core.c?h=ath11k-hibernation-support#n850

Quoted 4 lines

This code waits for the QMI messages to arrive and eventually timesout. But the impression I got from the start was that the mhi_power_up() always fails during resume. In our investigation, we confirmed that the failure is not happening at the MHI level.No, mhi_power_up() never fails as it only downloads PBL, SBL and waits

for mission mode, no MHI device created hence not affected by the deferred probe. However in addition to PBL/SBL, ath11k also needs to download m3.bin, borad.bin and regdb.bin. Those files are part of WLAN firmware and are downloaded via QMI messages. After mhi_power_up() succeeds ath11k_core_resume() waits for QMI downloading those files. As you know QMI relies on MHI channels, these channels are managed by qcom_mhi_qrtr_driver. Since device probing is deferred, qcom_mhi_qrtr_driver has no chance to run at this stage. As a result ath11k_core_resume() times out.

Quoted 4 lines

I'm not pointing fingers here, but trying to understand why can't you fix ath11k_core_resume() to not timeout? IMO this timeout should be handled as a deferral case.

Let's see what happens if we do it in a deferral way:
1. In ath11k_core_resume() we returns success directly without waiting for QMI downloading other firmware files.
2. Kernel unblocks device probe and schedules a work item to trigger all deferred probing. As a result MHI devices are probed by qcom_mhi_qrtr_driver and finally QMI is online.
3. kernel continues to resume and wake up userspace applications.
4. ath11k gets the message, either by kernel PM notification or something else, that QMI is ready and then downloads other firmware files.

What happens if userspace applications or network stack immediately initiate some WLAN request after resume back? Can ath11k handle such request? The answer is, most likely, no. Because there is no guarantee that QMI finishes downloading before those request.

Quoted 3 lines

- Mani

#12 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 36 lines · Baochen Qiang
Quoted 28 lines · Manivannan Sadhasivam

On Thu, Jan 04, 2024 at 11:39:12AM +0530, Manivannan Sadhasivam wrote:

+ Can, Qiang

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

Quoted 9 lines
Quoted 5 lines

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

We reproduced the issue with the help of PCIe team (thanks Can). What we found out was, during the resume from hibernation the faliure happens in ath11k_core_resume(). Precisely here:
https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/tree/drivers/net/wireless/ath/ath11k/core.c?h=ath11k-hibernation-support#n850

Quoted 5 lines

This code waits for the QMI messages to arrive and eventually timesout. But the impression I got from the start was that the mhi_power_up() always fails during resume. In our investigation, we confirmed that the failure is not happening at the MHI level.No, mhi_power_up() never fails as it only downloads PBL, SBL and waits

Quoted 9 lines

for mission mode, no MHI device created hence not affected by the deferred probe. However in addition to PBL/SBL, ath11k also needs to download m3.bin, borad.bin and regdb.bin. Those files are part of WLAN firmware and are downloaded via QMI messages. After mhi_power_up() succeeds ath11k_core_resume() waits for QMI downloading those files. As you know QMI relies on MHI channels, these channels are managed by qcom_mhi_qrtr_driver. Since device probing is deferred, qcom_mhi_qrtr_driver has no chance to run at this stage. As a result ath11k_core_resume() times out.

Thanks for the info, this clarifies the issue in detail.

Quoted 14 lines
Quoted 4 lines

I'm not pointing fingers here, but trying to understand why can't you fix ath11k_core_resume() to not timeout? IMO this timeout should be handled as a deferral case.

Let's see what happens if we do it in a deferral way:
1. In ath11k_core_resume() we returns success directly without waiting for QMI downloading other firmware files.
2. Kernel unblocks device probe and schedules a work item to trigger all deferred probing. As a result MHI devices are probed by qcom_mhi_qrtr_driver and finally QMI is online.
3. kernel continues to resume and wake up userspace applications.
4. ath11k gets the message, either by kernel PM notification or something else, that QMI is ready and then downloads other firmware files.

Quoted 5 lines

What happens if userspace applications or network stack immediately initiate some WLAN request after resume back? Can ath11k handle such request? The answer is, most likely, no. Because there is no guarantee that QMI finishes downloading before those request.

What will happen to userspace if ath11k returns an error like -EBUSY or something? Will the netdev completely go away?

- Mani

Quoted 3 lines
Quoted 3 lines

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#13 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 63 lines · Manivannan Sadhasivam
Quoted 36 lines · Baochen Qiang
Quoted 28 lines · Manivannan Sadhasivam

On Thu, Jan 04, 2024 at 11:39:12AM +0530, Manivannan Sadhasivam wrote:

+ Can, Qiang

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

Quoted 9 lines
Quoted 5 lines

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

We reproduced the issue with the help of PCIe team (thanks Can). What we found out was, during the resume from hibernation the faliure happens in ath11k_core_resume(). Precisely here:
https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/tree/drivers/net/wireless/ath/ath11k/core.c?h=ath11k-hibernation-support#n850

Quoted 5 lines

This code waits for the QMI messages to arrive and eventually timesout. But the impression I got from the start was that the mhi_power_up() always fails during resume. In our investigation, we confirmed that the failure is not happening at the MHI level.No, mhi_power_up() never fails as it only downloads PBL, SBL and waits

Quoted 9 lines

for mission mode, no MHI device created hence not affected by the deferred probe. However in addition to PBL/SBL, ath11k also needs to download m3.bin, borad.bin and regdb.bin. Those files are part of WLAN firmware and are downloaded via QMI messages. After mhi_power_up() succeeds ath11k_core_resume() waits for QMI downloading those files. As you know QMI relies on MHI channels, these channels are managed by qcom_mhi_qrtr_driver. Since device probing is deferred, qcom_mhi_qrtr_driver has no chance to run at this stage. As a result ath11k_core_resume() times out.

Thanks for the info, this clarifies the issue in detail.

Quoted 14 lines
Quoted 4 lines

I'm not pointing fingers here, but trying to understand why can't you fix ath11k_core_resume() to not timeout? IMO this timeout should be handled as a deferral case.

Let's see what happens if we do it in a deferral way:
1. In ath11k_core_resume() we returns success directly without waiting for QMI downloading other firmware files.
2. Kernel unblocks device probe and schedules a work item to trigger all deferred probing. As a result MHI devices are probed by qcom_mhi_qrtr_driver and finally QMI is online.
3. kernel continues to resume and wake up userspace applications.
4. ath11k gets the message, either by kernel PM notification or something else, that QMI is ready and then downloads other firmware files.

Quoted 8 lines
Quoted 5 lines

What happens if userspace applications or network stack immediately initiate some WLAN request after resume back? Can ath11k handle such request? The answer is, most likely, no. Because there is no guarantee that QMI finishes downloading before those request.

What will happen to userspace if ath11k returns an error like -EBUSY or something? Will the netdev completely go away?

It depends, and varies from application to application, we can't make the assumption.

Besides, it doesn't make sense to return -EBUSY or something like that, if ath11k returns success during resume. A WLAN driver is supposed to finish everything, at least get back to the state before suspend, in the resume callback. If it couldn't, report the error.

Quoted 7 lines

- Mani

Quoted 3 lines
Quoted 3 lines

- Mani

#14 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 74 lines · Baochen Qiang
Quoted 63 lines · Manivannan Sadhasivam
Quoted 36 lines · Baochen Qiang
Quoted 28 lines · Manivannan Sadhasivam

On Thu, Jan 04, 2024 at 11:39:12AM +0530, Manivannan Sadhasivam wrote:

+ Can, Qiang

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

To me it all sounds like the probe deferral is not handled properly in mac80211 stack. As you mentioned in the commit message that the dpm_prepare() blocks probing of devices. It gets unblocked and trigerred in dpm_complete():
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/base/power/main.c#n1131

So if mac80211/ath11k cannot probe the devices at the dpm_complete() stage, then it is definitely an issue that needs to be fixed properly.

Quoted 9 lines
Quoted 5 lines

To clarify, ath11k CAN probe the devices at dpm_complete() stage. The problem is kernel does not wait for all probes to finish, and in that way we will face the issue that user space applications are likely to fail because they get thawed BEFORE WLAN is ready.

Hmm. Please give me some time to reproduce this issue locally. I will get back to this thread with my analysis.

We reproduced the issue with the help of PCIe team (thanks Can). What we found out was, during the resume from hibernation the faliure happens in ath11k_core_resume(). Precisely here:
https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/tree/drivers/net/wireless/ath/ath11k/core.c?h=ath11k-hibernation-support#n850

Quoted 5 lines

This code waits for the QMI messages to arrive and eventually timesout. But the impression I got from the start was that the mhi_power_up() always fails during resume. In our investigation, we confirmed that the failure is not happening at the MHI level.No, mhi_power_up() never fails as it only downloads PBL, SBL and waits

Quoted 9 lines

for mission mode, no MHI device created hence not affected by the deferred probe. However in addition to PBL/SBL, ath11k also needs to download m3.bin, borad.bin and regdb.bin. Those files are part of WLAN firmware and are downloaded via QMI messages. After mhi_power_up() succeeds ath11k_core_resume() waits for QMI downloading those files. As you know QMI relies on MHI channels, these channels are managed by qcom_mhi_qrtr_driver. Since device probing is deferred, qcom_mhi_qrtr_driver has no chance to run at this stage. As a result ath11k_core_resume() times out.

Thanks for the info, this clarifies the issue in detail.

Quoted 14 lines
Quoted 4 lines

I'm not pointing fingers here, but trying to understand why can't you fix ath11k_core_resume() to not timeout? IMO this timeout should be handled as a deferral case.

Let's see what happens if we do it in a deferral way:
1. In ath11k_core_resume() we returns success directly without waiting for QMI downloading other firmware files.
2. Kernel unblocks device probe and schedules a work item to trigger all deferred probing. As a result MHI devices are probed by qcom_mhi_qrtr_driver and finally QMI is online.
3. kernel continues to resume and wake up userspace applications.
4. ath11k gets the message, either by kernel PM notification or something else, that QMI is ready and then downloads other firmware files.

Quoted 8 lines
Quoted 5 lines

What happens if userspace applications or network stack immediately initiate some WLAN request after resume back? Can ath11k handle such request? The answer is, most likely, no. Because there is no guarantee that QMI finishes downloading before those request.

What will happen to userspace if ath11k returns an error like -EBUSY or something? Will the netdev completely go away?

Quoted 8 lines

It depends, and varies from application to application, we can't make the assumption.

Besides, it doesn't make sense to return -EBUSY or something like that, if ath11k returns success during resume. A WLAN driver is supposed to finish everything, at least get back to the state before suspend, in the resume callback. If it couldn't, report the error.

Ok. So I am getting the feeling that we need to talk to the PM people to get a proper solution. Clearly fixing the MHI code is not the right thing to do. We might need a separate callback that gets registered by the drivers like ath11k to wait for the dependency drivers to get probed.

Can you initiate such a discussion? You can write to [email protected], "Rafael J. Wysocki" <[email protected]> and Pavel Machek <[email protected]>.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#15 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 7 lines · Manivannan Sadhasivam

Ok. So I am getting the feeling that we need to talk to the PM people to get a proper solution. Clearly fixing the MHI code is not the right thing to do. We might need a separate callback that gets registered by the drivers like ath11k to wait for the dependency drivers to get probed.

Can you initiate such a discussion? You can write to [email protected], "Rafael J. Wysocki" <[email protected]> and Pavel Machek <[email protected]>.

Please also include [email protected] since this topic may have ramifications across other Qualcomm technologies that use MHI.

/jeff

#16 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

In reply to Kalle Valo
Quoted 4 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

This is confusing! Maybe this is what confused me initially. mhi_sync_power_up() never fails, but ath11k timesout waiting for QMI. You also confirmed the same [1].

Quoted 130 lines

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/bus/mhi/host/init.c     |  1 +
 drivers/bus/mhi/host/internal.h |  1 +
 drivers/bus/mhi/host/pm.c       | 26 +++++++++++++++++++-------
 include/linux/mhi.h             | 29 +++++++++++++++++++++++++++--
 4 files changed, 48 insertions(+), 9 deletions(-)
drivers/bus/mhi/host/init.c +1 -0
index 65ceac1837f9..e626b03ffafa 100644
@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
 	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
 	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
 	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+	[DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
 };
 
 const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
drivers/bus/mhi/host/internal.h +1 -0
index 30ac415a3000..3f45c9c447bd 100644
@@ -69,6 +69,7 @@ enum dev_st_transition {
 	DEV_ST_TRANSITION_FP,
 	DEV_ST_TRANSITION_SYS_ERR,
 	DEV_ST_TRANSITION_DISABLE,
+	DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
 	DEV_ST_TRANSITION_MAX,
 };
 
drivers/bus/mhi/host/pm.c +16 -6
index a2f2feef1476..8833b0248393 100644
@@ -458,7 +458,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
 }
 
 /* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+				      bool destroy_device)
 {
 	enum mhi_pm_state cur_state;
 	struct mhi_event *mhi_event;
@@ -520,8 +521,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
 	dev_dbg(dev, "Waiting for all pending threads to complete\n");
 	wake_up_all(&mhi_cntrl->state_event);
 
-	dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	if (destroy_device) {
+		dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+		device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	}
 
 	mutex_lock(&mhi_cntrl->pm_mutex);
 
@@ -806,7 +809,10 @@ void mhi_pm_st_worker(struct work_struct *work)
 			mhi_pm_sys_error_transition(mhi_cntrl);
 			break;
 		case DEV_ST_TRANSITION_DISABLE:
-			mhi_pm_disable_transition(mhi_cntrl);
+			mhi_pm_disable_transition(mhi_cntrl, false);
+			break;
+		case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+			mhi_pm_disable_transition(mhi_cntrl, true);
 			break;
 		default:
 			break;
@@ -1160,7 +1166,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
 }
 EXPORT_SYMBOL_GPL(mhi_async_power_up);
 
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		      bool destroy_device)
 {
 	enum mhi_pm_state cur_state, transition_state;
 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1196,14 +1203,19 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
 	write_unlock_irq(&mhi_cntrl->pm_lock);
 	mutex_unlock(&mhi_cntrl->pm_mutex);
 
-	mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+	if (destroy_device)
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+	else

drivers/bus/mhi/host/pm.c void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) · line 1218

Quoted 10 lines
drivers/bus/mhi/host/pm.c +3 -1
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE);
 
 	/* Wait for shutdown to complete */
 	flush_work(&mhi_cntrl->st_worker);
 
 	disable_irq(mhi_cntrl->irq[0]);
 }
-EXPORT_SYMBOL_GPL(mhi_power_down);
+EXPORT_SYMBOL_GPL(__mhi_power_down);

This is a helper, so should not be exported. You should export the API instead.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 652

Quoted 13 lines
drivers/bus/mhi/host/pm.c +0 -0
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
 {
include/linux/mhi.h +2 -0
index d0f9b522f328..ae092bc8b97e 100644
@@ -648,12 +648,37 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
  */
 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
 
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		    bool destroy_device);

This is a helper, so make it static.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 656

Quoted 5 lines
include/linux/mhi.h +3 -1
+
 /**
- * mhi_power_down - Start MHI power down sequence
+ * mhi_power_down - Start MHI power down sequence. See also
+ * mhi_power_down_no_destroy() which is a variant of this for suspend.

suspend/hibernation

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 661

Quoted 6 lines
include/linux/mhi.h +2 -1
+ *
  * @mhi_cntrl: MHI controller
  * @graceful: Link is still accessible, so do a graceful shutdown process
  */
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)

No, API cannot be static inline. Make it global.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 668

Quoted 7 lines
include/linux/mhi.h +7 -0
+{
+	__mhi_power_down(mhi_cntrl, graceful, true);
+}
+
+/**
+ * mhi_power_down_no_destroy - Start MHI power down sequence but don't
+ * destroy struct devices. This is a variant for mhi_power_down() and is a

"struct devices for channels"

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 669

Quoted 1 line
include/linux/mhi.h +1 -0
+ * workaround to make it possible to use mhi_power_up() in a resume

You should mention that the devices are not destroyed and this would be useful in suspend/hibernation.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 671

Quoted 2 lines
include/linux/mhi.h +2 -0
+ * handler. When using this variant the caller must also call
+ * mhi_prepare_all_for_transfer_autoqueue() and

mhi_prepare_all_for_transfer*()

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 678

Quoted 7 lines
include/linux/mhi.h +7 -0
+ * mhi_unprepare_all_from_transfer().
+ *
+ * @mhi_cntrl: MHI controller
+ * @graceful: Link is still accessible, so do a graceful shutdown process
+ */
+static inline void mhi_power_down_no_destroy(struct mhi_controller *mhi_cntrl,
+					     bool graceful)

Same as above, make it global.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#17 lore raw

Re: [PATCH RFC v2 1/8] bus: mhi: host: add mhi_power_down_no_destroy()

Quoted 9 lines · Manivannan Sadhasivam
Quoted 4 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

If ath11k tries to call mhi_power_up() during resume it fails:

This is confusing! Maybe this is what confused me initially. mhi_sync_power_up() never fails, but ath11k timesout waiting for QMI. You also confirmed the same [1].

mhi_sync_power_up() creates MHI devices which fails to get probed. So from the view of ath11k it fails, while from the sense of code execuation it succeeds. Will rephrase to avoid confusion.

Quoted 209 lines
Quoted 131 lines

ath11k_pci 0000:06:00.0: timeout while waiting for restart complete

This happens because when calling mhi_power_up() the MHI subsystem eventually calls device_add() from mhi_create_devices() but the device creation is deferred:

mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral

The reason for deferring device creation is explained in dpm_prepare():

/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete(). */

Because the device probe is deferred, the qcom_mhi_qrtr_probe() is not called and qcom_mhi_qrtr_dl_callback() fails silently as qdev is zero:

static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
struct mhi_result *mhi_res)

{
	struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
	int rc;

	if (!qdev || mhi_res->transaction_status)
		return;

So what this means that QRTR is not delivering messages and the QMI connection is not working between ath11k and the firmware, resulting a failure in firmware initialisation.

To fix this add new function mhi_power_down_no_destroy() which does not destroy the devices during power down. This way mhi_power_up() can be called during resume and we can get ath11k hibernation working with the following patches.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
  drivers/bus/mhi/host/init.c     |  1 +
  drivers/bus/mhi/host/internal.h |  1 +
  drivers/bus/mhi/host/pm.c       | 26 +++++++++++++++++++-------
  include/linux/mhi.h             | 29 +++++++++++++++++++++++++++--
  4 files changed, 48 insertions(+), 9 deletions(-)
drivers/bus/mhi/host/init.c +1 -0
index 65ceac1837f9..e626b03ffafa 100644
@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
  	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
  	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
  	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+	[DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
  };
 
  const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
drivers/bus/mhi/host/internal.h +1 -0
index 30ac415a3000..3f45c9c447bd 100644
@@ -69,6 +69,7 @@ enum dev_st_transition {
  	DEV_ST_TRANSITION_FP,
  	DEV_ST_TRANSITION_SYS_ERR,
  	DEV_ST_TRANSITION_DISABLE,
+	DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
  	DEV_ST_TRANSITION_MAX,
  };
 
drivers/bus/mhi/host/pm.c +16 -6
index a2f2feef1476..8833b0248393 100644
@@ -458,7 +458,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
  }
 
  /* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+				      bool destroy_device)
  {
  	enum mhi_pm_state cur_state;
  	struct mhi_event *mhi_event;
@@ -520,8 +521,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
  	dev_dbg(dev, "Waiting for all pending threads to complete\n");
  	wake_up_all(&mhi_cntrl->state_event);
 
-	dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
-	device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	if (destroy_device) {
+		dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+		device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+	}
 
  	mutex_lock(&mhi_cntrl->pm_mutex);
 
@@ -806,7 +809,10 @@ void mhi_pm_st_worker(struct work_struct *work)
  			mhi_pm_sys_error_transition(mhi_cntrl);
  			break;
  		case DEV_ST_TRANSITION_DISABLE:
-			mhi_pm_disable_transition(mhi_cntrl);
+			mhi_pm_disable_transition(mhi_cntrl, false);
+			break;
+		case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+			mhi_pm_disable_transition(mhi_cntrl, true);
  			break;
  		default:
  			break;
@@ -1160,7 +1166,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
  }
  EXPORT_SYMBOL_GPL(mhi_async_power_up);
 
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		      bool destroy_device)
  {
  	enum mhi_pm_state cur_state, transition_state;
  	struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1196,14 +1203,19 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
  	write_unlock_irq(&mhi_cntrl->pm_lock);
  	mutex_unlock(&mhi_cntrl->pm_mutex);
 
-	mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+	if (destroy_device)
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+	else

drivers/bus/mhi/host/pm.c void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) · line 1218

Quoted 10 lines
drivers/bus/mhi/host/pm.c +3 -1
+		mhi_queue_state_transition(mhi_cntrl,
+					   DEV_ST_TRANSITION_DISABLE);
 
  	/* Wait for shutdown to complete */
  	flush_work(&mhi_cntrl->st_worker);
 
  	disable_irq(mhi_cntrl->irq[0]);
  }
-EXPORT_SYMBOL_GPL(mhi_power_down);
+EXPORT_SYMBOL_GPL(__mhi_power_down);

This is a helper, so should not be exported. You should export the API instead.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 652

Quoted 13 lines
drivers/bus/mhi/host/pm.c +0 -0
  int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
  {
include/linux/mhi.h +2 -0
index d0f9b522f328..ae092bc8b97e 100644
@@ -648,12 +648,37 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
   */
  int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
 
+void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+		    bool destroy_device);

This is a helper, so make it static.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 656

Quoted 5 lines
include/linux/mhi.h +3 -1
+
  /**
- * mhi_power_down - Start MHI power down sequence
+ * mhi_power_down - Start MHI power down sequence. See also
+ * mhi_power_down_no_destroy() which is a variant of this for suspend.

suspend/hibernation

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 661

Quoted 6 lines
include/linux/mhi.h +2 -1
+ *
   * @mhi_cntrl: MHI controller
   * @graceful: Link is still accessible, so do a graceful shutdown process
   */
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)

No, API cannot be static inline. Make it global.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 668

Quoted 7 lines
include/linux/mhi.h +7 -0
+{
+	__mhi_power_down(mhi_cntrl, graceful, true);
+}
+
+/**
+ * mhi_power_down_no_destroy - Start MHI power down sequence but don't
+ * destroy struct devices. This is a variant for mhi_power_down() and is a

"struct devices for channels"

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 669

Quoted 1 line
include/linux/mhi.h +1 -0
+ * workaround to make it possible to use mhi_power_up() in a resume

You should mention that the devices are not destroyed and this would be useful in suspend/hibernation.

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 671

Quoted 2 lines
include/linux/mhi.h +2 -0
+ * handler. When using this variant the caller must also call
+ * mhi_prepare_all_for_transfer_autoqueue() and

mhi_prepare_all_for_transfer*()

include/linux/mhi.h int mhi_async_power_up(struct mhi_controller *mhi_cntrl); · line 678

Quoted 7 lines
include/linux/mhi.h +7 -0
+ * mhi_unprepare_all_from_transfer().
+ *
+ * @mhi_cntrl: MHI controller
+ * @graceful: Link is still accessible, so do a graceful shutdown process
+ */
+static inline void mhi_power_down_no_destroy(struct mhi_controller *mhi_cntrl,
+					     bool graceful)

Same as above, make it global.

- Mani

author
#18 lore raw

RFC PATCH v2 2/8 bus: mhi: host: add new interfaces to handle MHI channels directly

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         |  20 ++++++-
 2 files changed, 126 insertions(+), 1 deletion(-)
drivers/bus/mhi/host/main.c +107 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
 }
 EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
 
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
 void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
 {
 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
 	}
 }
 EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +19 -1
index ae092bc8b97e..dcf62a57056a 100644
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
  * destroy struct devices. This is a variant for mhi_power_down() and is a
  * workaround to make it possible to use mhi_power_up() in a resume
  * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and
  * mhi_unprepare_all_from_transfer().
  *
  * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
  */
 bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
 
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
 #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
#19 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 21 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         |  20 ++++++-
 2 files changed, 126 insertions(+), 1 deletion(-)
drivers/bus/mhi/host/main.c +0 -0
index d80975f4bba8..3f677fc628ad 100644

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1672

Quoted 4 lines
drivers/bus/mhi/host/main.c +1 -0
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
 }
 EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
 
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 4 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 20 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
 void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
 {
 	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;

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

Quoted 5 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
 	}
 }
 EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

Quoted 58 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
  * destroy struct devices. This is a variant for mhi_power_down() and is a
  * workaround to make it possible to use mhi_power_up() in a resume
  * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 848

Quoted 11 lines
include/linux/mhi.h +4 -0
  * mhi_unprepare_all_from_transfer().
  *
  * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
  */
 bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
 
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
 #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#20 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 25 lines · Manivannan Sadhasivam
Quoted 24 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
  drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
  include/linux/mhi.h         |  20 ++++++-
  2 files changed, 126 insertions(+), 1 deletion(-)
drivers/bus/mhi/host/main.c +0 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
  }
  EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
 
Quoted 3 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1672

Quoted 1 line
drivers/bus/mhi/host/main.c +1 -0
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

Quoted 71 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 5 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 19 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
  void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
  {
  	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
Quoted 8 lines

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

Quoted 6 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
  	}
  }
  EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

same as above.

Quoted 106 lines
Quoted 59 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
   * destroy struct devices. This is a variant for mhi_power_down() and is a
   * workaround to make it possible to use mhi_power_up() in a resume
   * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 848

Quoted 11 lines
include/linux/mhi.h +4 -0
   * mhi_unprepare_all_from_transfer().
   *
   * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
   */
  bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
 
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
  #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
#21 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 31 lines · Baochen Qiang
Quoted 17 lines · Manivannan Sadhasivam
Quoted 16 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
  drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
  include/linux/mhi.h         |  20 ++++++-
  2 files changed, 126 insertions(+), 1 deletion(-)
Quoted 10 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1671

Quoted 8 lines
drivers/bus/mhi/host/main.c +1 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
  }
  EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

Quoted 5 lines

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

I think most of the checks in this function can be moved inside mhi_prepare_for_transfer() API. With that you can just reuse the API without adding a new helper.

For autoqueue channels, you can add another API mhi_prepare_all_for_transfer_autoqueue() just like mhi_prepare_for_transfer_autoqueue() to maintain uniformity.

- Mani

Quoted 187 lines
Quoted 71 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 5 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 19 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
  void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
  {
  	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
Quoted 8 lines

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

Quoted 6 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
  	}
  }
  EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

same as above.

Quoted 105 lines
Quoted 59 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
   * destroy struct devices. This is a variant for mhi_power_down() and is a
   * workaround to make it possible to use mhi_power_up() in a resume
   * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 847

Quoted 10 lines
include/linux/mhi.h +4 -0
   * mhi_unprepare_all_from_transfer().
   *
   * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
   */
  bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
  #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#22 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 42 lines · Manivannan Sadhasivam
Quoted 31 lines · Baochen Qiang
Quoted 17 lines · Manivannan Sadhasivam
Quoted 16 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
   drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
   include/linux/mhi.h         |  20 ++++++-
   2 files changed, 126 insertions(+), 1 deletion(-)
Quoted 10 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1671

Quoted 8 lines
drivers/bus/mhi/host/main.c +1 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
   }
   EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

Quoted 5 lines

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

I think most of the checks in this function can be moved inside mhi_prepare_for_transfer() API. With that you can just reuse the API without adding a new helper.

Quoted 5 lines

For autoqueue channels, you can add another API mhi_prepare_all_for_transfer_autoqueue() just like mhi_prepare_for_transfer_autoqueue() to maintain uniformity.

- Mani

If we do that, we need to call two APIs together, does it make sense? From the view of an MHI user, what we want is an API to prepare all channels, no matter whether a channel is configured as autoqueue or non-autoqueue, we don't care about it.

And besides, I don't think there is a scenario where we need to use them separately. So if we always need to use them together, why not merge them in a single API?

Quoted 189 lines
Quoted 188 lines
Quoted 72 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 6 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 19 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
   void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
   {
   	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
Quoted 8 lines

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

Quoted 6 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
   	}
   }
   EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

same as above.

Quoted 105 lines
Quoted 59 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
    * destroy struct devices. This is a variant for mhi_power_down() and is a
    * workaround to make it possible to use mhi_power_up() in a resume
    * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 847

Quoted 10 lines
include/linux/mhi.h +4 -0
    * mhi_unprepare_all_from_transfer().
    *
    * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
    */
   bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
   #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
#23 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 49 lines · Baochen Qiang
Quoted 46 lines · Manivannan Sadhasivam
Quoted 31 lines · Baochen Qiang
Quoted 17 lines · Manivannan Sadhasivam
Quoted 16 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
   drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
   include/linux/mhi.h         |  20 ++++++-
   2 files changed, 126 insertions(+), 1 deletion(-)
Quoted 10 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1671

Quoted 8 lines
drivers/bus/mhi/host/main.c +1 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
   }
   EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

Quoted 5 lines

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

I think most of the checks in this function can be moved inside mhi_prepare_for_transfer() API. With that you can just reuse the API without adding a new helper.

For autoqueue channels, you can add another API mhi_prepare_all_for_transfer_autoqueue() just like mhi_prepare_for_transfer_autoqueue() to maintain uniformity.

Quoted 6 lines
Quoted 1 line

- Mani

If we do that, we need to call two APIs together, does it make sense? From the view of an MHI user, what we want is an API to prepare all channels, no matter whether a channel is configured as autoqueue or non-autoqueue, we don't care about it.

You are calling this API from a wrong place first up. mhi_{prepare/unprepare}_transfer* APIs are meant to be used by the client drivers like QRTR. Controller drivers should not call them.

What you need here is the hibernation support for QRTR itself and call these APIs from there.

Quoted 4 lines

And besides, I don't think there is a scenario where we need to use them separately. So if we always need to use them together, why not merge them in a single API?

A single controller driver may expose multiple channels and those will bind to multiple client drivers. So only the client drivers should manage the channels, not the controller drivers themselves.

- Mani

Quoted 189 lines
Quoted 189 lines
Quoted 188 lines
Quoted 72 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 6 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 19 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
   void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
   {
   	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
Quoted 8 lines

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

Quoted 6 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
   	}
   }
   EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

same as above.

Quoted 105 lines
Quoted 59 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
    * destroy struct devices. This is a variant for mhi_power_down() and is a
    * workaround to make it possible to use mhi_power_up() in a resume
    * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 847

Quoted 10 lines
include/linux/mhi.h +4 -0
    * mhi_unprepare_all_from_transfer().
    *
    * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
    */
   bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
   #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#24 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 57 lines · Manivannan Sadhasivam
Quoted 49 lines · Baochen Qiang
Quoted 46 lines · Manivannan Sadhasivam
Quoted 31 lines · Baochen Qiang
Quoted 17 lines · Manivannan Sadhasivam
Quoted 16 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
    drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
    include/linux/mhi.h         |  20 ++++++-
    2 files changed, 126 insertions(+), 1 deletion(-)
Quoted 10 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1671

Quoted 8 lines
drivers/bus/mhi/host/main.c +1 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
    }
    EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

Quoted 5 lines

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

I think most of the checks in this function can be moved inside mhi_prepare_for_transfer() API. With that you can just reuse the API without adding a new helper.

For autoqueue channels, you can add another API mhi_prepare_all_for_transfer_autoqueue() just like mhi_prepare_for_transfer_autoqueue() to maintain uniformity.

Quoted 6 lines
Quoted 1 line

- Mani

If we do that, we need to call two APIs together, does it make sense? From the view of an MHI user, what we want is an API to prepare all channels, no matter whether a channel is configured as autoqueue or non-autoqueue, we don't care about it.

Quoted 6 lines

You are calling this API from a wrong place first up. mhi_{prepare/unprepare}_transfer* APIs are meant to be used by the client drivers like QRTR. Controller drivers should not call them.

What you need here is the hibernation support for QRTR itself and call these APIs from there.

OK, I got your point. QRTR is the right place to manage MHI channels, not ath11k it self. And we even don't need these two APIs if change to do it in QRTR.

Quoted 9 lines
Quoted 5 lines

And besides, I don't think there is a scenario where we need to use them separately. So if we always need to use them together, why not merge them in a single API?

A single controller driver may expose multiple channels and those will bind to multiple client drivers. So only the client drivers should manage the channels, not the controller drivers themselves.

Exactly.

Great thanks for the proposal, Mani. Will change accordingly in next version.

Quoted 193 lines

- Mani

Quoted 189 lines
Quoted 189 lines
Quoted 188 lines
Quoted 72 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1676

Quoted 6 lines
drivers/bus/mhi/host/main.c +4 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;

Reverse Xmas order, please.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1683

Quoted 7 lines
drivers/bus/mhi/host/main.c +7 -0
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only prepare virtual devices that are attached to bus */

"Only prepare virtual devices for the channels". Here and below.

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1689

Quoted 6 lines
drivers/bus/mhi/host/main.c +6 -0
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, we are not allowed to do prepare for it.
+	 */

Use the preferred style for comment:

	/*
	 * ...
	 */
Quoted 8 lines
drivers/bus/mhi/host/main.c +8 -0
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1707

Quoted 10 lines
drivers/bus/mhi/host/main.c +10 -0
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan && ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+		return 0;
+
+

Remove extra newline.

Quoted 19 lines
drivers/bus/mhi/host/main.c +16 -0
+	if (dl_chan && ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+		return 0;
+
+	if (dl_chan->pre_alloc)
+		return mhi_prepare_for_transfer_autoqueue(mhi_dev);
+	else
+		return mhi_prepare_for_transfer(mhi_dev);
+}
+
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_prepare_for_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_prepare_all_for_transfer);
+
    void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
    {
    	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
Quoted 8 lines

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

Quoted 6 lines
drivers/bus/mhi/host/main.c +2 -0
@@ -1684,3 +1736,58 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
    	}
    }
    EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+
+static int ____mhi_unprepare_from_transfer(struct device *dev, void *data)

__mhi_unprepare_all_from_transfer

same as above.

Quoted 105 lines
Quoted 59 lines
drivers/bus/mhi/host/main.c +53 -0
+{
+	struct mhi_device *mhi_dev;
+	struct mhi_chan *ul_chan, *dl_chan;
+	enum mhi_ee_type ee = MHI_EE_MAX;
+
+	if (dev->bus != &mhi_bus_type)
+		return 0;
+
+	mhi_dev = to_mhi_device(dev);
+
+	/* Only unprepare virtual devices that are attached to bus */
+	if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+		return 0;
+
+	/* There are cases where there is no MHI client driver matches
+	 * this device, so it is not probed or prepared, no need to
+	 * do unprepare for it.
+	 */
+	if (!mhi_dev->id)
+		return 0;
+
+	ul_chan = mhi_dev->ul_chan;
+	dl_chan = mhi_dev->dl_chan;
+
+	/*
+	 * If execution environment is specified, remove only those devices that
+	 * started in them based on ee_mask for the channels as we move on to a
+	 * different execution environment
+	 */
+	if (data)
+		ee = *(enum mhi_ee_type *)data;
+
+	if (ul_chan) {
+		if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	if (dl_chan) {
+		if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee)))
+			return 0;
+	}
+
+	mhi_unprepare_from_transfer(mhi_dev);
+
+	return 0;
+}
+
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl)
+{
+	return device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL,
+				     ____mhi_unprepare_from_transfer);
+}
+EXPORT_SYMBOL_GPL(mhi_unprepare_all_from_transfer);
include/linux/mhi.h +0 -0
index ae092bc8b97e..dcf62a57056a 100644

include/linux/mhi.h static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu · line 671

Quoted 5 lines
include/linux/mhi.h +1 -1
@@ -668,7 +668,7 @@ static inline void mhi_power_down(struct mhi_controller *mhi_cntrl, bool gracefu
     * destroy struct devices. This is a variant for mhi_power_down() and is a
     * workaround to make it possible to use mhi_power_up() in a resume
     * handler. When using this variant the caller must also call
- * mhi_prepare_all_for_transfer_autoqueue() and
+ * mhi_prepare_all_for_transfer() and

This change belongs to previous patch.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 847

Quoted 10 lines
include/linux/mhi.h +4 -0
     * mhi_unprepare_all_from_transfer().
     *
     * @mhi_cntrl: MHI controller
@@ -842,4 +842,22 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
     */
    bool mhi_queue_is_full(struct mhi_device *mhi_dev, enum dma_data_direction dir);
+/**
+ * mhi_prepare_all_for_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this needs to be called after
+ * calling mhi_power_up().

Add info about what this API does also.

include/linux/mhi.h int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, · line 857

Quoted 9 lines
include/linux/mhi.h +9 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_prepare_all_for_transfer(struct mhi_controller *mhi_cntrl);
+
+/**
+ * mhi_unprepare_all_from_transfer - if you are using
+ * mhi_power_down_no_destroy() variant this function needs to be called
+ * before calling mhi_power_down_no_destroy().

Same as above.

- Mani

Quoted 10 lines
include/linux/mhi.h +5 -0
+ *
+ * @mhi_cntrl: MHI controller
+ */
+int mhi_unprepare_all_from_transfer(struct mhi_controller *mhi_cntrl);
+
    #endif /* _MHI_H_ */
Signature · 1 line
2.39.2
#25 lore raw

Re: [PATCH RFC v2 2/8] bus: mhi: host: add new interfaces to handle MHI channels directly

Quoted 77 lines · Baochen Qiang
Quoted 57 lines · Manivannan Sadhasivam
Quoted 49 lines · Baochen Qiang
Quoted 46 lines · Manivannan Sadhasivam
Quoted 31 lines · Baochen Qiang
Quoted 17 lines · Manivannan Sadhasivam
Quoted 16 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

When using mhi_power_down_no_destroy() MHI hosts need to unprepare MHI channels by themselves. Similarly, MHI stack will also not create new MHI device since old devices were not destroyed, so MHI hosts need to prepare channels as well. Hence add these two interfaces to make that possible.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
    drivers/bus/mhi/host/main.c | 107 ++++++++++++++++++++++++++++++++++++
    include/linux/mhi.h         |  20 ++++++-
    2 files changed, 126 insertions(+), 1 deletion(-)
Quoted 10 lines

drivers/bus/mhi/host/main.c int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) · line 1671

Quoted 8 lines
drivers/bus/mhi/host/main.c +1 -0
index d80975f4bba8..3f677fc628ad 100644
@@ -1669,6 +1669,58 @@ int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev)
    }
    EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue);
+static int ____mhi_prepare_for_transfer(struct device *dev, void *data)

"__mhi_prepare_all_for_transfer"

Quoted 5 lines

This is to prepare one single child device, I don't think a name like __mhi_prepare_all_for_transfer (with 'all' inside) make sense, right? How about changing to "mhi_prepare_dev_for_transfer" or "mhi_prepare_single_for_transfer"?

I think most of the checks in this function can be moved inside mhi_prepare_for_transfer() API. With that you can just reuse the API without adding a new helper.

For autoqueue channels, you can add another API mhi_prepare_all_for_transfer_autoqueue() just like mhi_prepare_for_transfer_autoqueue() to maintain uniformity.

Quoted 6 lines
Quoted 1 line

- Mani

If we do that, we need to call two APIs together, does it make sense? From the view of an MHI user, what we want is an API to prepare all channels, no matter whether a channel is configured as autoqueue or non-autoqueue, we don't care about it.

Quoted 6 lines

You are calling this API from a wrong place first up. mhi_{prepare/unprepare}_transfer* APIs are meant to be used by the client drivers like QRTR. Controller drivers should not call them.

What you need here is the hibernation support for QRTR itself and call these APIs from there.

OK, I got your point. QRTR is the right place to manage MHI channels, not ath11k it self.
And we even don't need these two APIs if change to do it in QRTR.

Quoted 6 lines
Quoted 5 lines

And besides, I don't think there is a scenario where we need to use them separately. So if we always need to use them together, why not merge them in a single API?

Quoted 8 lines
Quoted 3 lines

A single controller driver may expose multiple channels and those will bind to multiple client drivers. So only the client drivers should manage the channels, not the controller drivers themselves.

Exactly.

Great thanks for the proposal, Mani. Will change accordingly in next version.

And you can drop the RFC tag in the version.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
author
#26 lore raw

RFC PATCH v2 3/8 wifi: ath11k: handle irq enable/disable in several code path

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

For non WoW suspend/resume, ath11k host powers down whole hardware when suspend and power up it when resume, the code path it goes through is very like the ath11k reset logic.

In order to reuse that logic, do some IRQ management work to make it work.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/core.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/net/wireless/ath/ath11k/core.c +3 -7
index 0c6ecbb9a066..fbd6b6a0e12c 100644
@@ -852,9 +852,6 @@ int ath11k_core_resume(struct ath11k_base *ab)
 		return ret;
 	}
 
-	ath11k_hif_ce_irq_enable(ab);
-	ath11k_hif_irq_enable(ab);
-
 	ret = ath11k_dp_rx_pktlog_start(ab);
 	if (ret) {
 		ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
@@ -1775,10 +1772,9 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
 
 	mutex_lock(&ab->core_lock);
 	ath11k_thermal_unregister(ab);
-	ath11k_hif_irq_disable(ab);
 	ath11k_dp_pdev_free(ab);
 	ath11k_spectral_deinit(ab);
-	ath11k_hif_stop(ab);
+	ath11k_ce_cleanup_pipes(ab);
 	ath11k_wmi_detach(ab);
 	ath11k_dp_pdev_reo_cleanup(ab);
 	mutex_unlock(&ab->core_lock);
@@ -2033,8 +2029,8 @@ static void ath11k_core_reset(struct work_struct *work)
 	time_left = wait_for_completion_timeout(&ab->recovery_start,
 						ATH11K_RECOVER_START_TIMEOUT_HZ);
 
-	ath11k_hif_power_down(ab);
-	ath11k_hif_power_up(ab);
+	ath11k_hif_irq_disable(ab);
+	ath11k_hif_ce_irq_disable(ab);
 
 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n");
 }
Signature · 1 line
2.39.2
author
#27 lore raw

RFC PATCH v2 4/8 wifi: ath11k: remove MHI LOOPBACK channels

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

There is no driver to match these two channels, so remove them. This fixes warnings from MHI subsystem during suspend:

mhi mhi0_LOOPBACK: 1: Failed to reset channel, still resetting mhi mhi0_LOOPBACK: 0: Failed to reset channel, still resetting

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/mhi.c | 56 ---------------------------
 1 file changed, 56 deletions(-)
drivers/net/wireless/ath/ath11k/mhi.c +0 -56
index afeabd6ecc67..579af57f7377 100644
@@ -21,34 +21,6 @@
 #define RDDM_DUMP_SIZE	0x420000
 
 static struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
-	{
-		.num = 0,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 0,
-		.dir = DMA_TO_DEVICE,
-		.ee_mask = 0x4,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
-	{
-		.num = 1,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 0,
-		.dir = DMA_FROM_DEVICE,
-		.ee_mask = 0x4,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
 	{
 		.num = 20,
 		.name = "IPCR",
@@ -114,34 +86,6 @@ static struct mhi_controller_config ath11k_mhi_config_qca6390 = {
 };
 
 static struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
-	{
-		.num = 0,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 1,
-		.dir = DMA_TO_DEVICE,
-		.ee_mask = 0x14,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
-	{
-		.num = 1,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 1,
-		.dir = DMA_FROM_DEVICE,
-		.ee_mask = 0x14,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
 	{
 		.num = 20,
 		.name = "IPCR",
Signature · 1 line
2.39.2
#28 lore raw

Re: [PATCH RFC v2 4/8] wifi: ath11k: remove MHI LOOPBACK channels

Quoted 7 lines · Kalle Valo

From: Baochen Qiang <[email protected]>

There is no driver to match these two channels, so remove them. This fixes warnings from MHI subsystem during suspend:

mhi mhi0_LOOPBACK: 1: Failed to reset channel, still resetting mhi mhi0_LOOPBACK: 0: Failed to reset channel, still resetting

With v2, these warnings are gone even without this patch. so it should be removed from commit log. It's enough to only mention that those channels are not used.

Quoted 83 lines

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
  drivers/net/wireless/ath/ath11k/mhi.c | 56 ---------------------------
  1 file changed, 56 deletions(-)
drivers/net/wireless/ath/ath11k/mhi.c +0 -56
index afeabd6ecc67..579af57f7377 100644
@@ -21,34 +21,6 @@
  #define RDDM_DUMP_SIZE	0x420000
 
  static struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
-	{
-		.num = 0,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 0,
-		.dir = DMA_TO_DEVICE,
-		.ee_mask = 0x4,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
-	{
-		.num = 1,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 0,
-		.dir = DMA_FROM_DEVICE,
-		.ee_mask = 0x4,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
  	{
  		.num = 20,
  		.name = "IPCR",
@@ -114,34 +86,6 @@ static struct mhi_controller_config ath11k_mhi_config_qca6390 = {
  };
 
  static struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
-	{
-		.num = 0,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 1,
-		.dir = DMA_TO_DEVICE,
-		.ee_mask = 0x14,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
-	{
-		.num = 1,
-		.name = "LOOPBACK",
-		.num_elements = 32,
-		.event_ring = 1,
-		.dir = DMA_FROM_DEVICE,
-		.ee_mask = 0x14,
-		.pollcfg = 0,
-		.doorbell = MHI_DB_BRST_DISABLE,
-		.lpm_notify = false,
-		.offload_channel = false,
-		.doorbell_mode_switch = false,
-		.auto_queue = false,
-	},
  	{
  		.num = 20,
  		.name = "IPCR",
Signature · 3 lines
ath11k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath11k
author
#29 lore raw

RFC PATCH v2 5/8 wifi: ath11k: do not dump SRNG statistics during resume

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

Both the firmware reset feature and the power management suspend/resume feature share common power-down and power-up functionality. One aspect of the power-up functionality is the handling of the ATH11K_QMI_EVENT_FW_INIT_DONE event. When this event is received, a call is made to ath11k_hal_dump_srng_stats(), with the purpose to collect information that may be useful in debugging the cause of a firmware reset.

Unfortunately, since this functionality is shared between both the firmware reset path and the power management resume path, the kernel log is flooded with messages during resume. Since these messages are not useful during resume, and in fact can be confusing and can increase the time it takes to resume, update the logic to only call ath11k_hal_dump_srng_stats() during firmware reset.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/qmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/ath/ath11k/qmi.c +2 -1
index c270dc46d506..97a74563d4a6 100644
@@ -3249,7 +3249,8 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
 		case ATH11K_QMI_EVENT_FW_INIT_DONE:
 			clear_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags);
 			if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) {
-				ath11k_hal_dump_srng_stats(ab);
+				if (ab->is_reset)
+					ath11k_hal_dump_srng_stats(ab);
 				queue_work(ab->workqueue, &ab->restart_work);
 				break;
 			}
Signature · 1 line
2.39.2
author
#30 lore raw

RFC PATCH v2 6/8 wifi: ath11k: fix warning on DMA ring capabilities event

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

We are seeing below warning in both reset and suspend/resume scenarios:

[69663.691847] ath11k_pci 0000:02:00.0: Already processed, so ignoring dma ring caps

This is because ab->num_db_cap is not cleared in ath11k_wmi_free_dbring_caps(), so clear it to avoid such warnings.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/wmi.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/ath/ath11k/wmi.c +1 -0
index 2845b4313d3a..b73d4286f7d3 100644
@@ -4786,6 +4786,7 @@ static void ath11k_wmi_free_dbring_caps(struct ath11k_base *ab)
 {
 	kfree(ab->db_caps);
 	ab->db_caps = NULL;
+	ab->num_db_cap = 0;
 }
 
 static int ath11k_wmi_tlv_dma_ring_caps(struct ath11k_base *ab,
Signature · 1 line
2.39.2
author
#31 lore raw

RFC PATCH v2 7/8 wifi: ath11k: thermal: don't try to register multiple times

In reply to Kalle Valo

From: Kalle Valo <[email protected]>

Every time the firmware boots we call ath11k_core_qmi_firmware_ready() which ends up calling ath11k_thermal_register(). So we try to register thermal devices multiple times. And when we power off the firmware during suspend/hibernation (implemented in the next patch) we get a warning in resume:

hwmon hwmon4: PM: parent phy0 should not be sleeping

Workaround this similarly like ath11k_mac_register() does by testing ATH11K_FLAG_REGISTERED.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/thermal.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/wireless/ath/ath11k/thermal.c +3 -0
index c9b012f97ba5..80abf472fb87 100644
@@ -162,6 +162,9 @@ int ath11k_thermal_register(struct ath11k_base *ab)
 	struct ath11k_pdev *pdev;
 	int i, ret;
 
+	if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
+		return 0;
+
 	for (i = 0; i < ab->num_radios; i++) {
 		pdev = &ab->pdevs[i];
 		ar = pdev->ar;
Signature · 1 line
2.39.2
author
#32 lore raw

RFC PATCH v2 8/8 wifi: ath11k: support hibernation

In reply to Kalle Valo

From: Baochen Qiang <[email protected]>

Now that all infrastructure is in place and ath11k is fixed to handle all the corner cases, power down the ath11k firmware during suspend and power it back up during resume. This fixes the problem when using hibernation with ath11k PCI devices.

Change to use ath11k_hif_power_down() instead of ath11k_hif_suspend() in suspend callback and to use ath11k_hif_power_up() instead of ath11k_hif_resume() in resume callback.

In ath11k_hif_power_down(), we reset MHI channels to keep from unexpected activities, and last we go PCI power down path to completely reset whole hardware. Most importantly in power down path, we tell mhi_power_down() to not to destroy MHI devices, making us get rid of the probe-defer issue when resume.

In ath11k_hif_power_up(), we go normal PCI power up path to download firmware etc. Since MHI channels are not activated automatically, we do it manually as the last part.

Also change related code due to interface changes.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

---
 drivers/net/wireless/ath/ath11k/ahb.c  |  8 ++--
 drivers/net/wireless/ath/ath11k/core.c | 34 ++++++++--------
 drivers/net/wireless/ath/ath11k/core.h |  2 +
 drivers/net/wireless/ath/ath11k/hif.h  | 12 +++---
 drivers/net/wireless/ath/ath11k/mhi.c  | 21 +++++++++-
 drivers/net/wireless/ath/ath11k/mhi.h  |  4 +-
 drivers/net/wireless/ath/ath11k/pci.c  | 55 +++++++++++++++++++++++---
 drivers/net/wireless/ath/ath11k/qmi.c  |  4 +-
 8 files changed, 103 insertions(+), 37 deletions(-)
drivers/net/wireless/ath/ath11k/ahb.c +4 -4
index f8f5e653cd03..0ff5e85392aa 100644
@@ -401,7 +401,7 @@ static void ath11k_ahb_stop(struct ath11k_base *ab)
 	ath11k_ce_cleanup_pipes(ab);
 }
 
-static int ath11k_ahb_power_up(struct ath11k_base *ab)
+static int ath11k_ahb_power_up(struct ath11k_base *ab, bool is_resume)
 {
 	struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
 	int ret;
@@ -413,11 +413,11 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab)
 	return ret;
 }
 
-static void ath11k_ahb_power_down(struct ath11k_base *ab)
+static int ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)
 {
 	struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
 
-	rproc_shutdown(ab_ahb->tgt_rproc);
+	return rproc_shutdown(ab_ahb->tgt_rproc);
 }
 
 static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab)
@@ -1256,7 +1256,7 @@ static int ath11k_ahb_remove(struct platform_device *pdev)
 	struct ath11k_base *ab = platform_get_drvdata(pdev);
 
 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
-		ath11k_ahb_power_down(ab);
+		ath11k_ahb_power_down(ab, false);
 		ath11k_debugfs_soc_destroy(ab);
 		ath11k_qmi_deinit_service(ab);
 		goto qmi_fail;
drivers/net/wireless/ath/ath11k/core.c +18 -16
index fbd6b6a0e12c..0e0b02692282 100644
@@ -800,12 +800,6 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 		return ret;
 	}
 
-	ret = ath11k_wow_enable(ab);
-	if (ret) {
-		ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret);
-		return ret;
-	}
-
 	ret = ath11k_dp_rx_pktlog_stop(ab, false);
 	if (ret) {
 		ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
@@ -819,7 +813,7 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 	ath11k_hif_irq_disable(ab);
 	ath11k_hif_ce_irq_disable(ab);
 
-	ret = ath11k_hif_suspend(ab);
+	ret = ath11k_hif_power_down(ab, true);
 	if (ret) {
 		ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
 		return ret;
@@ -834,6 +828,7 @@ int ath11k_core_resume(struct ath11k_base *ab)
 	int ret;
 	struct ath11k_pdev *pdev;
 	struct ath11k *ar;
+	long time_left;
 
 	if (!ab->hw_params.supports_suspend)
 		return -EOPNOTSUPP;
@@ -846,11 +841,18 @@ int ath11k_core_resume(struct ath11k_base *ab)
 	if (!ar || ar->state != ATH11K_STATE_OFF)
 		return 0;
 
-	ret = ath11k_hif_resume(ab);
+	reinit_completion(&ab->restart_completed);
+	ret = ath11k_hif_power_up(ab, true);
 	if (ret) {
 		ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret);
 		return ret;
 	}
+	time_left = wait_for_completion_timeout(&ab->restart_completed,
+						ATH11K_RESET_TIMEOUT_HZ);
+	if (time_left == 0) {
+		ath11k_warn(ab, "timeout while waiting for restart complete");
+		return -ETIMEDOUT;
+	}
 
 	ret = ath11k_dp_rx_pktlog_start(ab);
 	if (ret) {
@@ -859,12 +861,6 @@ int ath11k_core_resume(struct ath11k_base *ab)
 		return ret;
 	}
 
-	ret = ath11k_wow_wakeup(ab);
-	if (ret) {
-		ath11k_warn(ab, "failed to wakeup wow during resume: %d\n", ret);
-		return ret;
-	}
-
 	return 0;
 }
 EXPORT_SYMBOL(ath11k_core_resume);
@@ -1488,7 +1484,7 @@ static int ath11k_core_soc_create(struct ath11k_base *ab)
 		goto err_qmi_deinit;
 	}
 
-	ret = ath11k_hif_power_up(ab);
+	ret = ath11k_hif_power_up(ab, false);
 	if (ret) {
 		ath11k_err(ab, "failed to power up :%d\n", ret);
 		goto err_debugfs_reg;
@@ -1963,6 +1959,8 @@ static void ath11k_core_restart(struct work_struct *work)
 
 	if (!ab->is_reset)
 		ath11k_core_post_reconfigure_recovery(ab);
+
+	complete(&ab->restart_completed);
 }
 
 static void ath11k_core_reset(struct work_struct *work)
@@ -2032,6 +2030,9 @@ static void ath11k_core_reset(struct work_struct *work)
 	ath11k_hif_irq_disable(ab);
 	ath11k_hif_ce_irq_disable(ab);
 
+	ath11k_hif_power_down(ab, false);
+	ath11k_hif_power_up(ab, false);
+
 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n");
 }
 
@@ -2102,7 +2103,7 @@ void ath11k_core_deinit(struct ath11k_base *ab)
 
 	mutex_unlock(&ab->core_lock);
 
-	ath11k_hif_power_down(ab);
+	ath11k_hif_power_down(ab, false);
 	ath11k_mac_destroy(ab);
 	ath11k_core_soc_destroy(ab);
 	ath11k_fw_destroy(ab);
@@ -2155,6 +2156,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
 	timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
 	init_completion(&ab->htc_suspend);
 	init_completion(&ab->wow.wakeup_completed);
+	init_completion(&ab->restart_completed);
 
 	ab->dev = dev;
 	ab->hif.bus = bus;
drivers/net/wireless/ath/ath11k/core.h +2 -0
index 7e3b6779f4e9..b3816b8be934 100644
@@ -995,6 +995,8 @@ struct ath11k_base {
 		DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT);
 	} fw;
 
+	struct completion restart_completed;
+
 #ifdef CONFIG_NL80211_TESTMODE
 	struct {
 		u32 data_pos;
drivers/net/wireless/ath/ath11k/hif.h +6 -6
index d68ed4214dec..7f08591ed5c8 100644
@@ -16,8 +16,8 @@ struct ath11k_hif_ops {
 	void (*irq_disable)(struct ath11k_base *ab);
 	int (*start)(struct ath11k_base *ab);
 	void (*stop)(struct ath11k_base *ab);
-	int (*power_up)(struct ath11k_base *ab);
-	void (*power_down)(struct ath11k_base *ab);
+	int (*power_up)(struct ath11k_base *ab, bool is_resume);
+	int (*power_down)(struct ath11k_base *ab, bool is_suspend);
 	int (*suspend)(struct ath11k_base *ab);
 	int (*resume)(struct ath11k_base *ab);
 	int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id,
@@ -64,14 +64,14 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab)
 	ab->hif.ops->irq_disable(ab);
 }
 
-static inline int ath11k_hif_power_up(struct ath11k_base *ab)
+static inline int ath11k_hif_power_up(struct ath11k_base *ab, bool is_resume)
 {
-	return ab->hif.ops->power_up(ab);
+	return ab->hif.ops->power_up(ab, is_resume);
 }
 
-static inline void ath11k_hif_power_down(struct ath11k_base *ab)
+static inline int ath11k_hif_power_down(struct ath11k_base *ab, bool is_resume)
 {
-	ab->hif.ops->power_down(ab);
+	return ab->hif.ops->power_down(ab, is_resume);
 }
 
 static inline int ath11k_hif_suspend(struct ath11k_base *ab)
drivers/net/wireless/ath/ath11k/mhi.c +19 -2
index 579af57f7377..58b92f7ce2b8 100644
@@ -441,9 +441,16 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci)
 	return 0;
 }
 
-void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
+void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)
 {
-	mhi_power_down(ab_pci->mhi_ctrl, true);
+	/* During suspend we need to use mhi_power_down_no_destroy()
+	 * workaround, otherwise mhi_power_up() will fail during resume.
+	 */
+	if (is_suspend)
+		mhi_power_down_no_destroy(ab_pci->mhi_ctrl, true);
+	else
+		mhi_power_down(ab_pci->mhi_ctrl, true);
+
 	mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
 }
 
@@ -478,3 +485,13 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
 
 	return 0;
 }
+
+int ath11k_mhi_prepare_for_transfer(struct ath11k_pci *ab_pci)
+{
+	return mhi_prepare_all_for_transfer(ab_pci->mhi_ctrl);
+}
+
+int ath11k_mhi_unprepare_from_transfer(struct ath11k_pci *ab_pci)
+{
+	return mhi_unprepare_all_from_transfer(ab_pci->mhi_ctrl);
+}
drivers/net/wireless/ath/ath11k/mhi.h +3 -1
index 8d9f852da695..80902fda5842 100644
@@ -17,7 +17,7 @@
 #define MHICTRL_RESET_MASK			0x2
 
 int ath11k_mhi_start(struct ath11k_pci *ar_pci);
-void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
+void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend);
 int ath11k_mhi_register(struct ath11k_pci *ar_pci);
 void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
 void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
@@ -26,4 +26,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab);
 int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
 int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
 
+int ath11k_mhi_prepare_for_transfer(struct ath11k_pci *ar_pci);
+int ath11k_mhi_unprepare_from_transfer(struct ath11k_pci *ar_pci);
 #endif
drivers/net/wireless/ath/ath11k/pci.c +49 -6

89 lines not on this page · raw message

drivers/net/wireless/ath/ath11k/qmi.c +2 -2
index 97a74563d4a6..7d856d8b7f89 100644
@@ -2877,8 +2877,8 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab)
 	}
 
 	/* reset the firmware */
-	ath11k_hif_power_down(ab);
-	ath11k_hif_power_up(ab);
+	ath11k_hif_power_down(ab, false);
+	ath11k_hif_power_up(ab, false);
 	ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n");
 	return 0;
 }
Signature · 1 line
2.39.2
#33 lore raw
In reply to Kalle Valo
Quoted 74 lines · Kalle Valo

From: Kalle Valo <[email protected]>

Currently in ath11k we keep the firmware running on the WLAN device when the network interface (wlan0) is down. The problem is that this will break hibernation, obviously the firmware can't be running after the whole system is powered off. To power down the ath11k firmware for suspend/hibernation some changes both in MHI subsystem and ath11k is needed.

This patchset fixes a longstanding bug report about broken hibernation support:

https://bugzilla.kernel.org/show_bug.cgi?id=214649

This patchset is marked as RFC as it requires changes in MHI subsystem. Also this has been tested only on WCN6855, need to test also on more AP based chipsets like IPQ8074 and QCN9074.

The patches are also available at:

https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/log/?h=ath11k-hibernation-support

Earlier versions of this patchset have been tested by multiple users with positive results.

v2:

* rebase to ath-202311221826 (6.7.0-rc2-wt-ath+)

* 'bus: mhi: host: add mhi_power_down_no_destroy()': fix null state string for DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': fix typos in comments

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': honour initial autoqueue configuration

* 'bus: mhi: host: add new interfaces to handle MHI channels directly': don't prepare/unprepare MHI devices that don't match with a MHI client driver

* 'wifi: ath11k: remove MHI LOOPBACK channels': remove LOOPBACK channels for QCN9074 as well

v1: https://lore.kernel.org/mhi/[email protected]/

Baochen Qiang (7):
bus: mhi: host: add mhi_power_down_no_destroy()
bus: mhi: host: add new interfaces to handle MHI channels directly
wifi: ath11k: handle irq enable/disable in several code path
wifi: ath11k: remove MHI LOOPBACK channels
wifi: ath11k: do not dump SRNG statistics during resume
wifi: ath11k: fix warning on DMA ring capabilities event
wifi: ath11k: support hibernation

Kalle Valo (1):
wifi: ath11k: thermal: don't try to register multiple times

 drivers/bus/mhi/host/init.c               |   1 +
 drivers/bus/mhi/host/internal.h           |   1 +
 drivers/bus/mhi/host/main.c               | 107 ++++++++++++++++++++++
 drivers/bus/mhi/host/pm.c                 |  26 ++++--
 drivers/net/wireless/ath/ath11k/ahb.c     |   8 +-
 drivers/net/wireless/ath/ath11k/core.c    |  44 +++++----
 drivers/net/wireless/ath/ath11k/core.h    |   2 +
 drivers/net/wireless/ath/ath11k/hif.h     |  12 +--
 drivers/net/wireless/ath/ath11k/mhi.c     |  77 ++++------------
 drivers/net/wireless/ath/ath11k/mhi.h     |   4 +-
 drivers/net/wireless/ath/ath11k/pci.c     |  55 +++++++++--
 drivers/net/wireless/ath/ath11k/qmi.c     |   7 +-
 drivers/net/wireless/ath/ath11k/thermal.c |   3 +
 drivers/net/wireless/ath/ath11k/wmi.c     |   1 +
 include/linux/mhi.h                       |  47 +++++++++-
 15 files changed, 285 insertions(+), 110 deletions(-)
Quoted 1 line

base-commit: 16a212b4f33c4edd9ce9a9e0953b5389216e8ed9

Series LGTM. Will add appropriate tags when this is no longer RFC

Signature · 3 lines
ath11k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath11k