]> git.ipfire.org Git - thirdparty/openwrt.git/blob
75c87e378bb448c302af4bb0fe31cff3f3ec7ba1
[thirdparty/openwrt.git] /
1 From 3471bba1b33a8b54cb0be9d30b7aac4fb2766365 Mon Sep 17 00:00:00 2001
2 From: Baochen Qiang <quic_bqiang@quicinc.com>
3 Date: Tue, 5 Mar 2024 10:13:18 +0800
4 Subject: [PATCH] bus: mhi: host: Add mhi_power_down_keep_dev() API to support
5 system suspend/hibernation
6
7 Currently, ath11k fails to resume from system suspend/hibernation on some
8 the x86 host machines with below error message:
9
10 ```
11 ath11k_pci 0000:06:00.0: timeout while waiting for restart complete
12 ```
13
14 This happens because, ath11k powers down the MHI stack during suspend and
15 that leads to destruction of the struct device associated with the MHI
16 channels. And during resume, ath11k calls calling mhi_sync_power_up() to
17 power up the MHI subsystem and that eventually calls the driver framework's
18 device_add() API from mhi_create_devices(). But the PM framework blocks the
19 struct device creation during device_add() and this leads to probe deferral
20 as below:
21
22 ```
23 mhi mhi0_IPCR: Driver qcom_mhi_qrtr force probe deferral
24 ```
25
26 The reason for deferring device creation during resume is explained in
27 dpm_prepare():
28
29 /*
30 * It is unsafe if probing of devices will happen during suspend or
31 * hibernation and system behavior will be unpredictable in this
32 * case. So, let's prohibit device's probing here and defer their
33 * probes instead. The normal behavior will be restored in
34 * dpm_complete().
35 */
36
37 Due to the device probe deferral, qcom_mhi_qrtr_probe() API is not getting
38 called during resume and thus MHI channels are not prepared. So this blocks
39 the QMI messages from being transferred between ath11k and firmware,
40 resulting in a firmware initialization failure.
41
42 After consulting with Rafael, it was decided to not destroy the struct
43 device for the MHI channels during system suspend/hibernation because the
44 device is bound to appear again during resume.
45
46 So to achieve this, a new API called mhi_power_down_keep_dev() is
47 introduced for MHI controllers to keep the struct device when required.
48 This API is similar to the existing mhi_power_down() API, except that it
49 keeps the struct device associated with MHI channels instead of destroying
50 them.
51
52 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30
53
54 Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
55 Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
56 Reviewed-by: Jeff Johnson <quic_jjohnson@quicinc.com>
57 Link: https://lore.kernel.org/r/20240305021320.3367-2-quic_bqiang@quicinc.com
58 [mani: reworded the commit message and subject]
59 Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
60 ---
61 drivers/bus/mhi/host/init.c | 1 +
62 drivers/bus/mhi/host/internal.h | 1 +
63 drivers/bus/mhi/host/pm.c | 42 ++++++++++++++++++++++++++++-----
64 include/linux/mhi.h | 18 +++++++++++++-
65 4 files changed, 55 insertions(+), 7 deletions(-)
66
67 --- a/drivers/bus/mhi/host/init.c
68 +++ b/drivers/bus/mhi/host/init.c
69 @@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DE
70 [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
71 [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
72 [DEV_ST_TRANSITION_DISABLE] = "DISABLE",
73 + [DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
74 };
75
76 const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
77 --- a/drivers/bus/mhi/host/internal.h
78 +++ b/drivers/bus/mhi/host/internal.h
79 @@ -69,6 +69,7 @@ enum dev_st_transition {
80 DEV_ST_TRANSITION_FP,
81 DEV_ST_TRANSITION_SYS_ERR,
82 DEV_ST_TRANSITION_DISABLE,
83 + DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
84 DEV_ST_TRANSITION_MAX,
85 };
86
87 --- a/drivers/bus/mhi/host/pm.c
88 +++ b/drivers/bus/mhi/host/pm.c
89 @@ -466,7 +466,8 @@ error_mission_mode:
90 }
91
92 /* Handle shutdown transitions */
93 -static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
94 +static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
95 + bool destroy_device)
96 {
97 enum mhi_pm_state cur_state;
98 struct mhi_event *mhi_event;
99 @@ -528,8 +529,16 @@ skip_mhi_reset:
100 dev_dbg(dev, "Waiting for all pending threads to complete\n");
101 wake_up_all(&mhi_cntrl->state_event);
102
103 - dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
104 - device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
105 + /*
106 + * Only destroy the 'struct device' for channels if indicated by the
107 + * 'destroy_device' flag. Because, during system suspend or hibernation
108 + * state, there is no need to destroy the 'struct device' as the endpoint
109 + * device would still be physically attached to the machine.
110 + */
111 + if (destroy_device) {
112 + dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
113 + device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
114 + }
115
116 mutex_lock(&mhi_cntrl->pm_mutex);
117
118 @@ -820,7 +829,10 @@ void mhi_pm_st_worker(struct work_struct
119 mhi_pm_sys_error_transition(mhi_cntrl);
120 break;
121 case DEV_ST_TRANSITION_DISABLE:
122 - mhi_pm_disable_transition(mhi_cntrl);
123 + mhi_pm_disable_transition(mhi_cntrl, false);
124 + break;
125 + case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
126 + mhi_pm_disable_transition(mhi_cntrl, true);
127 break;
128 default:
129 break;
130 @@ -1174,7 +1186,8 @@ error_exit:
131 }
132 EXPORT_SYMBOL_GPL(mhi_async_power_up);
133
134 -void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
135 +static void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
136 + bool destroy_device)
137 {
138 enum mhi_pm_state cur_state, transition_state;
139 struct device *dev = &mhi_cntrl->mhi_dev->dev;
140 @@ -1210,15 +1223,32 @@ void mhi_power_down(struct mhi_controlle
141 write_unlock_irq(&mhi_cntrl->pm_lock);
142 mutex_unlock(&mhi_cntrl->pm_mutex);
143
144 - mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
145 + if (destroy_device)
146 + mhi_queue_state_transition(mhi_cntrl,
147 + DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
148 + else
149 + mhi_queue_state_transition(mhi_cntrl,
150 + DEV_ST_TRANSITION_DISABLE);
151
152 /* Wait for shutdown to complete */
153 flush_work(&mhi_cntrl->st_worker);
154
155 disable_irq(mhi_cntrl->irq[0]);
156 }
157 +
158 +void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
159 +{
160 + __mhi_power_down(mhi_cntrl, graceful, true);
161 +}
162 EXPORT_SYMBOL_GPL(mhi_power_down);
163
164 +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl,
165 + bool graceful)
166 +{
167 + __mhi_power_down(mhi_cntrl, graceful, false);
168 +}
169 +EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev);
170 +
171 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
172 {
173 int ret = mhi_async_power_up(mhi_cntrl);
174 --- a/include/linux/mhi.h
175 +++ b/include/linux/mhi.h
176 @@ -649,13 +649,29 @@ int mhi_async_power_up(struct mhi_contro
177 int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
178
179 /**
180 - * mhi_power_down - Start MHI power down sequence
181 + * mhi_power_down - Power down the MHI device and also destroy the
182 + * 'struct device' for the channels associated with it.
183 + * See also mhi_power_down_keep_dev() which is a variant
184 + * of this API that keeps the 'struct device' for channels
185 + * (useful during suspend/hibernation).
186 * @mhi_cntrl: MHI controller
187 * @graceful: Link is still accessible, so do a graceful shutdown process
188 */
189 void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
190
191 /**
192 + * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct
193 + * device' for the channels associated with it.
194 + * This is a variant of 'mhi_power_down()' and
195 + * useful in scenarios such as suspend/hibernation
196 + * where destroying of the 'struct device' is not
197 + * needed.
198 + * @mhi_cntrl: MHI controller
199 + * @graceful: Link is still accessible, so do a graceful shutdown process
200 + */
201 +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful);
202 +
203 +/**
204 * mhi_unprepare_after_power_down - Free any allocated memory after power down
205 * @mhi_cntrl: MHI controller
206 */