Message ID | 20240228022243.17762-2-quic_bqiang@quicinc.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kalle Valo |
Headers | show |
Series | wifi: ath11k: hibernation support | expand |
On 2/27/2024 6:22 PM, Baochen Qiang wrote: > ath11k fails to resume: > > ath11k_pci 0000:06:00.0: timeout while waiting for restart complete > > This happens because when calling mhi_sync_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 thus MHI channels are not prepared: > > 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 > initialization. > > To fix this add new function mhi_power_down_keep_dev() which doesn't destroy > the devices for channels during power down. This way we avoid probe defer issue > and finally 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 > > Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> > Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com> Reviewed-by: Jeff Johnson <quic_jjohnson@quicinc.com>
On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: > ath11k fails to resume: > > ath11k_pci 0000:06:00.0: timeout while waiting for restart complete > > This happens because when calling mhi_sync_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 thus MHI channels are not prepared: > > 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 > initialization. > > To fix this add new function mhi_power_down_keep_dev() which doesn't destroy > the devices for channels during power down. This way we avoid probe defer issue > and finally 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 > > Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> Did Kalle co-author this patch? If so, his Co-developed-by tag should be added. > Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com> Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> - Mani > --- > drivers/bus/mhi/host/internal.h | 4 +++- > drivers/bus/mhi/host/pm.c | 42 ++++++++++++++++++++++++++++----- > include/linux/mhi.h | 18 +++++++++++++- > 3 files changed, 56 insertions(+), 8 deletions(-) > > diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h > index 5fe49311b8eb..aaad40a07f69 100644 > --- a/drivers/bus/mhi/host/internal.h > +++ b/drivers/bus/mhi/host/internal.h > @@ -80,6 +80,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, > }; > > @@ -90,7 +91,8 @@ enum dev_st_transition { > dev_st_trans(MISSION_MODE, "MISSION MODE") \ > dev_st_trans(FP, "FLASH PROGRAMMER") \ > dev_st_trans(SYS_ERR, "SYS ERROR") \ > - dev_st_trans_end(DISABLE, "DISABLE") > + dev_st_trans(DISABLE, "DISABLE") \ > + dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)") > > extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; > #define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ > diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c > index 8b40d3f01acc..11c0e751f223 100644 > --- a/drivers/bus/mhi/host/pm.c > +++ b/drivers/bus/mhi/host/pm.c > @@ -468,7 +468,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; > @@ -530,8 +531,16 @@ 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); > + /* > + * Only destroy the 'struct device' for channels if indicated by the > + * 'destroy_device' flag. Because, during system suspend or hibernation > + * state, there is no need to destroy the 'struct device' as the endpoint > + * device would still be physically attached to the machine. > + */ > + 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); > > @@ -821,7 +830,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; > @@ -1175,7 +1187,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) > +static 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; > @@ -1211,15 +1224,32 @@ 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]); > } > + > +void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) > +{ > + __mhi_power_down(mhi_cntrl, graceful, true); > +} > EXPORT_SYMBOL_GPL(mhi_power_down); > > +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, > + bool graceful) > +{ > + __mhi_power_down(mhi_cntrl, graceful, false); > +} > +EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev); > + > int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) > { > int ret = mhi_async_power_up(mhi_cntrl); > diff --git a/include/linux/mhi.h b/include/linux/mhi.h > index 77b8c0a26674..cde01e133a1b 100644 > --- a/include/linux/mhi.h > +++ b/include/linux/mhi.h > @@ -630,12 +630,28 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl); > int mhi_sync_power_up(struct mhi_controller *mhi_cntrl); > > /** > - * mhi_power_down - Start MHI power down sequence > + * mhi_power_down - Power down the MHI device and also destroy the > + * 'struct device' for the channels associated with it. > + * See also mhi_power_down_keep_dev() which is a variant > + * of this API that keeps the 'struct device' for channels > + * (useful during suspend/hibernation). > * @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); > > +/** > + * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct > + * device' for the channels associated with it. > + * This is a variant of 'mhi_power_down()' and > + * useful in scenarios such as suspend/hibernation > + * where destroying of the 'struct device' is not > + * needed. > + * @mhi_cntrl: MHI controller > + * @graceful: Link is still accessible, so do a graceful shutdown process > + */ > +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful); > + > /** > * mhi_unprepare_after_power_down - Free any allocated memory after power down > * @mhi_cntrl: MHI controller > -- > 2.25.1 >
On 2/29/2024 6:12 PM, Manivannan Sadhasivam wrote: > On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: >> ath11k fails to resume: >> >> ath11k_pci 0000:06:00.0: timeout while waiting for restart complete >> >> This happens because when calling mhi_sync_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 thus MHI channels are not prepared: >> >> 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 >> initialization. >> >> To fix this add new function mhi_power_down_keep_dev() which doesn't destroy >> the devices for channels during power down. This way we avoid probe defer issue >> and finally 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 >> >> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> > > Did Kalle co-author this patch? If so, his Co-developed-by tag should be > added. Hmm, I'm not sure... I would like Kalle's thoughts on this. > >> Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com> > > Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> > > - Mani > >> --- >> drivers/bus/mhi/host/internal.h | 4 +++- >> drivers/bus/mhi/host/pm.c | 42 ++++++++++++++++++++++++++++----- >> include/linux/mhi.h | 18 +++++++++++++- >> 3 files changed, 56 insertions(+), 8 deletions(-) >> >> diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h >> index 5fe49311b8eb..aaad40a07f69 100644 >> --- a/drivers/bus/mhi/host/internal.h >> +++ b/drivers/bus/mhi/host/internal.h >> @@ -80,6 +80,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, >> }; >> >> @@ -90,7 +91,8 @@ enum dev_st_transition { >> dev_st_trans(MISSION_MODE, "MISSION MODE") \ >> dev_st_trans(FP, "FLASH PROGRAMMER") \ >> dev_st_trans(SYS_ERR, "SYS ERROR") \ >> - dev_st_trans_end(DISABLE, "DISABLE") >> + dev_st_trans(DISABLE, "DISABLE") \ >> + dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)") >> >> extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; >> #define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ >> diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c >> index 8b40d3f01acc..11c0e751f223 100644 >> --- a/drivers/bus/mhi/host/pm.c >> +++ b/drivers/bus/mhi/host/pm.c >> @@ -468,7 +468,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; >> @@ -530,8 +531,16 @@ 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); >> + /* >> + * Only destroy the 'struct device' for channels if indicated by the >> + * 'destroy_device' flag. Because, during system suspend or hibernation >> + * state, there is no need to destroy the 'struct device' as the endpoint >> + * device would still be physically attached to the machine. >> + */ >> + 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); >> >> @@ -821,7 +830,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; >> @@ -1175,7 +1187,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) >> +static 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; >> @@ -1211,15 +1224,32 @@ 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]); >> } >> + >> +void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) >> +{ >> + __mhi_power_down(mhi_cntrl, graceful, true); >> +} >> EXPORT_SYMBOL_GPL(mhi_power_down); >> >> +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, >> + bool graceful) >> +{ >> + __mhi_power_down(mhi_cntrl, graceful, false); >> +} >> +EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev); >> + >> int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) >> { >> int ret = mhi_async_power_up(mhi_cntrl); >> diff --git a/include/linux/mhi.h b/include/linux/mhi.h >> index 77b8c0a26674..cde01e133a1b 100644 >> --- a/include/linux/mhi.h >> +++ b/include/linux/mhi.h >> @@ -630,12 +630,28 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl); >> int mhi_sync_power_up(struct mhi_controller *mhi_cntrl); >> >> /** >> - * mhi_power_down - Start MHI power down sequence >> + * mhi_power_down - Power down the MHI device and also destroy the >> + * 'struct device' for the channels associated with it. >> + * See also mhi_power_down_keep_dev() which is a variant >> + * of this API that keeps the 'struct device' for channels >> + * (useful during suspend/hibernation). >> * @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); >> >> +/** >> + * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct >> + * device' for the channels associated with it. >> + * This is a variant of 'mhi_power_down()' and >> + * useful in scenarios such as suspend/hibernation >> + * where destroying of the 'struct device' is not >> + * needed. >> + * @mhi_cntrl: MHI controller >> + * @graceful: Link is still accessible, so do a graceful shutdown process >> + */ >> +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful); >> + >> /** >> * mhi_unprepare_after_power_down - Free any allocated memory after power down >> * @mhi_cntrl: MHI controller >> -- >> 2.25.1 >> >
Baochen Qiang <quic_bqiang@quicinc.com> writes: > On 2/29/2024 6:12 PM, Manivannan Sadhasivam wrote: >> On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: >>> ath11k fails to resume: >>> >>> ath11k_pci 0000:06:00.0: timeout while waiting for restart complete >>> >>> This happens because when calling mhi_sync_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 thus MHI channels are not prepared: >>> >>> 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 >>> initialization. >>> >>> To fix this add new function mhi_power_down_keep_dev() which doesn't destroy >>> the devices for channels during power down. This way we avoid probe defer issue >>> and finally 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 >>> >>> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> >> >> Did Kalle co-author this patch? If so, his Co-developed-by tag should >> be added. > > Hmm, I'm not sure... I would like Kalle's thoughts on this. IIRC I did only some simple cleanup before submitting the patch so I don't think Co-developed-by is justified. I'm also fine with removing my Signed-off-by.
On 3/1/2024 3:35 AM, Kalle Valo wrote: > Baochen Qiang <quic_bqiang@quicinc.com> writes: > >> On 2/29/2024 6:12 PM, Manivannan Sadhasivam wrote: >>> On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: >>>> ath11k fails to resume: >>>> >>>> ath11k_pci 0000:06:00.0: timeout while waiting for restart complete >>>> >>>> This happens because when calling mhi_sync_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 thus MHI channels are not prepared: >>>> >>>> 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 >>>> initialization. >>>> >>>> To fix this add new function mhi_power_down_keep_dev() which doesn't destroy >>>> the devices for channels during power down. This way we avoid probe defer issue >>>> and finally 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 >>>> >>>> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> >>> >>> Did Kalle co-author this patch? If so, his Co-developed-by tag should >>> be added. >> >> Hmm, I'm not sure... I would like Kalle's thoughts on this. > > IIRC I did only some simple cleanup before submitting the patch so I > don't think Co-developed-by is justified. I'm also fine with removing my > Signed-off-by. Thanks Kalle. Hi Mani, so according to Kalle's comments, I'd like to keep the patch as is. >
On Fri, Mar 01, 2024 at 10:04:06AM +0800, Baochen Qiang wrote: > > > On 3/1/2024 3:35 AM, Kalle Valo wrote: > > Baochen Qiang <quic_bqiang@quicinc.com> writes: > > > > > On 2/29/2024 6:12 PM, Manivannan Sadhasivam wrote: > > > > On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: > > > > > ath11k fails to resume: > > > > > > > > > > ath11k_pci 0000:06:00.0: timeout while waiting for restart complete > > > > > > > > > > This happens because when calling mhi_sync_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 thus MHI channels are not prepared: > > > > > > > > > > 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 > > > > > initialization. > > > > > > > > > > To fix this add new function mhi_power_down_keep_dev() which doesn't destroy > > > > > the devices for channels during power down. This way we avoid probe defer issue > > > > > and finally 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 > > > > > > > > > > Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> > > > > > > > > Did Kalle co-author this patch? If so, his Co-developed-by tag should > > > > be added. > > > > > > Hmm, I'm not sure... I would like Kalle's thoughts on this. > > > > IIRC I did only some simple cleanup before submitting the patch so I > > don't think Co-developed-by is justified. I'm also fine with removing my > > Signed-off-by. > Thanks Kalle. > > Hi Mani, so according to Kalle's comments, I'd like to keep the patch as is. > No. Either remove his signed off by (as indicated by Kalle) or add a co-developed-by tag. Keeping just a signed-off-by tag is wrong. - Mani
On 3/1/2024 8:25 PM, Manivannan Sadhasivam wrote: > On Fri, Mar 01, 2024 at 10:04:06AM +0800, Baochen Qiang wrote: >> >> >> On 3/1/2024 3:35 AM, Kalle Valo wrote: >>> Baochen Qiang <quic_bqiang@quicinc.com> writes: >>> >>>> On 2/29/2024 6:12 PM, Manivannan Sadhasivam wrote: >>>>> On Wed, Feb 28, 2024 at 10:22:41AM +0800, Baochen Qiang wrote: >>>>>> ath11k fails to resume: >>>>>> >>>>>> ath11k_pci 0000:06:00.0: timeout while waiting for restart complete >>>>>> >>>>>> This happens because when calling mhi_sync_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 thus MHI channels are not prepared: >>>>>> >>>>>> 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 >>>>>> initialization. >>>>>> >>>>>> To fix this add new function mhi_power_down_keep_dev() which doesn't destroy >>>>>> the devices for channels during power down. This way we avoid probe defer issue >>>>>> and finally 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 >>>>>> >>>>>> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> >>>>> >>>>> Did Kalle co-author this patch? If so, his Co-developed-by tag should >>>>> be added. >>>> >>>> Hmm, I'm not sure... I would like Kalle's thoughts on this. >>> >>> IIRC I did only some simple cleanup before submitting the patch so I >>> don't think Co-developed-by is justified. I'm also fine with removing my >>> Signed-off-by. >> Thanks Kalle. >> >> Hi Mani, so according to Kalle's comments, I'd like to keep the patch as is. >> > > No. Either remove his signed off by (as indicated by Kalle) or add a > co-developed-by tag. Keeping just a signed-off-by tag is wrong. > OK, will send new version with Kalle's s-o-b tag removed. > - Mani >
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 5fe49311b8eb..aaad40a07f69 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -80,6 +80,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, }; @@ -90,7 +91,8 @@ enum dev_st_transition { dev_st_trans(MISSION_MODE, "MISSION MODE") \ dev_st_trans(FP, "FLASH PROGRAMMER") \ dev_st_trans(SYS_ERR, "SYS ERROR") \ - dev_st_trans_end(DISABLE, "DISABLE") + dev_st_trans(DISABLE, "DISABLE") \ + dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)") extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; #define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index 8b40d3f01acc..11c0e751f223 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -468,7 +468,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; @@ -530,8 +531,16 @@ 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); + /* + * Only destroy the 'struct device' for channels if indicated by the + * 'destroy_device' flag. Because, during system suspend or hibernation + * state, there is no need to destroy the 'struct device' as the endpoint + * device would still be physically attached to the machine. + */ + 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); @@ -821,7 +830,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; @@ -1175,7 +1187,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) +static 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; @@ -1211,15 +1224,32 @@ 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]); } + +void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) +{ + __mhi_power_down(mhi_cntrl, graceful, true); +} EXPORT_SYMBOL_GPL(mhi_power_down); +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, + bool graceful) +{ + __mhi_power_down(mhi_cntrl, graceful, false); +} +EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev); + int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) { int ret = mhi_async_power_up(mhi_cntrl); diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 77b8c0a26674..cde01e133a1b 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -630,12 +630,28 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl); int mhi_sync_power_up(struct mhi_controller *mhi_cntrl); /** - * mhi_power_down - Start MHI power down sequence + * mhi_power_down - Power down the MHI device and also destroy the + * 'struct device' for the channels associated with it. + * See also mhi_power_down_keep_dev() which is a variant + * of this API that keeps the 'struct device' for channels + * (useful during suspend/hibernation). * @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); +/** + * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct + * device' for the channels associated with it. + * This is a variant of 'mhi_power_down()' and + * useful in scenarios such as suspend/hibernation + * where destroying of the 'struct device' is not + * needed. + * @mhi_cntrl: MHI controller + * @graceful: Link is still accessible, so do a graceful shutdown process + */ +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful); + /** * mhi_unprepare_after_power_down - Free any allocated memory after power down * @mhi_cntrl: MHI controller