From 813e0ae613d6ee1b3e11f1c41f8b9e9df8ef0493 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 5 Mar 2024 10:13:18 +0800 Subject: bus: mhi: host: Add mhi_power_down_keep_dev() API to support system suspend/hibernation Currently, ath11k fails to resume from system suspend/hibernation on some the x86 host machines with below error message: ``` ath11k_pci 0000:06:00.0: timeout while waiting for restart complete ``` This happens because, ath11k powers down the MHI stack during suspend and that leads to destruction of the struct device associated with the MHI channels. And during resume, ath11k calls calling mhi_sync_power_up() to power up the MHI subsystem and that eventually calls the driver framework's device_add() API from mhi_create_devices(). But the PM framework blocks the struct device creation during device_add() and this leads to probe deferral as below: ``` mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral ``` The reason for deferring device creation during resume 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(). */ Due to the device probe deferral, qcom_mhi_qrtr_probe() API is not getting called during resume and thus MHI channels are not prepared. So this blocks the QMI messages from being transferred between ath11k and firmware, resulting in a firmware initialization failure. After consulting with Rafael, it was decided to not destroy the struct device for the MHI channels during system suspend/hibernation because the device is bound to appear again during resume. So to achieve this, a new API called mhi_power_down_keep_dev() is introduced for MHI controllers to keep the struct device when required. This API is similar to the existing mhi_power_down() API, except that it keeps the struct device associated with MHI channels instead of destroying them. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Signed-off-by: Baochen Qiang Reviewed-by: Manivannan Sadhasivam Reviewed-by: Jeff Johnson Link: https://lore.kernel.org/r/20240305021320.3367-2-quic_bqiang@quicinc.com [mani: reworded the commit message and subject] Signed-off-by: Manivannan Sadhasivam --- include/linux/mhi.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux') 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 -- cgit v1.2.3 From 17553ba8e19dee8770b3dcc597d49dcc3418f3b0 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Wed, 24 Apr 2024 11:21:53 +0800 Subject: bus: mhi: host: Add sysfs entry to force device to enter EDL Add sysfs entry to allow users of MHI bus to force device to enter EDL (Emergency Download) mode to download the device firmware. Since there is no guarantee that all the devices will support EDL mode, the sysfs entry is kept as an optional one and will appear only for the supported devices. Controllers supporting the EDL mode are expected to provide edl_trigger() callback that puts the device into EDL mode. Signed-off-by: Qiang Yu Reviewed-by: Jeffrey Hugo Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1713928915-18229-2-git-send-email-quic_qianyu@quicinc.com [mani: fixed the kernel version and reworded the commit message] Signed-off-by: Manivannan Sadhasivam --- include/linux/mhi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mhi.h b/include/linux/mhi.h index cde01e133a1b..d968e1ab44dc 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -353,6 +353,7 @@ struct mhi_controller_config { * @read_reg: Read a MHI register via the physical link (required) * @write_reg: Write a MHI register via the physical link (required) * @reset: Controller specific reset function (optional) + * @edl_trigger: CB function to trigger EDL mode (optional) * @buffer_len: Bounce buffer length * @index: Index of the MHI controller instance * @bounce_buf: Use of bounce buffer @@ -435,6 +436,7 @@ struct mhi_controller { void (*write_reg)(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 val); void (*reset)(struct mhi_controller *mhi_cntrl); + int (*edl_trigger)(struct mhi_controller *mhi_cntrl); size_t buffer_len; int index; -- cgit v1.2.3 From 553f94fc7667259e47a9318e9e5702c9a814d637 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Wed, 24 Apr 2024 11:21:54 +0800 Subject: bus: mhi: host: Add a new API for getting channel doorbell offset Some controllers may want to access a specific doorbell register. Hence add a new API that reads the CHDBOFF register and returns the offset of the doorbell registers from MMIO base, so that the controller can calculate the address of the specific doorbell register by adding the register offset with doorbell offset and MMIO base address. Signed-off-by: Qiang Yu Reviewed-by: Jeffrey Hugo Link: https://lore.kernel.org/r/1713928915-18229-3-git-send-email-quic_qianyu@quicinc.com [mani: reworded commit message and Kdoc] Signed-off-by: Manivannan Sadhasivam --- include/linux/mhi.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mhi.h b/include/linux/mhi.h index d968e1ab44dc..b573f15762f8 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -816,4 +816,13 @@ 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_get_channel_doorbell_offset - Get the channel doorbell offset + * @mhi_cntrl: MHI controller + * @chdb_offset: Read channel doorbell offset + * + * Return: 0 if the read succeeds, a negative error code otherwise + */ +int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset); + #endif /* _MHI_H_ */ -- cgit v1.2.3