1
2
3
4
5
6#include "iosm_ipc_protocol.h"
7
8
9#define IPC_PM_ACTIVE_TIMEOUT_MS (500)
10
11
12
13
14#define IPC_PM_SLEEP (0)
15#define CONSUME_STATE (0)
16#define IPC_PM_ACTIVE (1)
17
18void ipc_pm_signal_hpda_doorbell(struct iosm_pm *ipc_pm, u32 identifier,
19 bool host_slp_check)
20{
21 if (host_slp_check && ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE &&
22 ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE_WAIT) {
23 ipc_pm->pending_hpda_update = true;
24 dev_dbg(ipc_pm->dev,
25 "Pend HPDA update set. Host PM_State: %d identifier:%d",
26 ipc_pm->host_pm_state, identifier);
27 return;
28 }
29
30 if (!ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_IRQ, true)) {
31 ipc_pm->pending_hpda_update = true;
32 dev_dbg(ipc_pm->dev, "Pending HPDA update set. identifier:%d",
33 identifier);
34 return;
35 }
36 ipc_pm->pending_hpda_update = false;
37
38
39 ipc_cp_irq_hpda_update(ipc_pm->pcie, identifier);
40
41 ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_IRQ, false);
42}
43
44
45static bool ipc_pm_link_activate(struct iosm_pm *ipc_pm)
46{
47 if (ipc_pm->cp_state == IPC_MEM_DEV_PM_ACTIVE)
48 return true;
49
50 if (ipc_pm->cp_state == IPC_MEM_DEV_PM_SLEEP) {
51 if (ipc_pm->ap_state == IPC_MEM_DEV_PM_SLEEP) {
52
53 ipc_cp_irq_sleep_control(ipc_pm->pcie,
54 IPC_MEM_DEV_PM_WAKEUP);
55 ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE_WAIT;
56
57 goto not_active;
58 }
59
60 if (ipc_pm->ap_state == IPC_MEM_DEV_PM_ACTIVE_WAIT)
61 goto not_active;
62
63 return true;
64 }
65
66not_active:
67
68 return false;
69}
70
71bool ipc_pm_wait_for_device_active(struct iosm_pm *ipc_pm)
72{
73 bool ret_val = false;
74
75 if (ipc_pm->ap_state != IPC_MEM_DEV_PM_ACTIVE) {
76
77 smp_mb__before_atomic();
78
79
80
81
82 set_bit(0, &ipc_pm->host_sleep_pend);
83
84
85 smp_mb__after_atomic();
86
87 if (!wait_for_completion_interruptible_timeout
88 (&ipc_pm->host_sleep_complete,
89 msecs_to_jiffies(IPC_PM_ACTIVE_TIMEOUT_MS))) {
90 dev_err(ipc_pm->dev,
91 "PM timeout. Expected State:%d. Actual: %d",
92 IPC_MEM_DEV_PM_ACTIVE, ipc_pm->ap_state);
93 goto active_timeout;
94 }
95 }
96
97 ret_val = true;
98active_timeout:
99
100 smp_mb__before_atomic();
101
102
103
104
105 clear_bit(0, &ipc_pm->host_sleep_pend);
106
107
108 smp_mb__after_atomic();
109
110 return ret_val;
111}
112
113static void ipc_pm_on_link_sleep(struct iosm_pm *ipc_pm)
114{
115
116
117
118 ipc_pm->cp_state = IPC_MEM_DEV_PM_SLEEP;
119 ipc_pm->ap_state = IPC_MEM_DEV_PM_SLEEP;
120
121 ipc_cp_irq_sleep_control(ipc_pm->pcie, IPC_MEM_DEV_PM_SLEEP);
122}
123
124static void ipc_pm_on_link_wake(struct iosm_pm *ipc_pm, bool ack)
125{
126 ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
127
128 if (ack) {
129 ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
130
131 ipc_cp_irq_sleep_control(ipc_pm->pcie, IPC_MEM_DEV_PM_ACTIVE);
132
133
134 if (test_bit(CONSUME_STATE, &ipc_pm->host_sleep_pend))
135 complete(&ipc_pm->host_sleep_complete);
136 }
137
138
139
140
141
142
143
144 if (ipc_pm->pending_hpda_update &&
145 ipc_pm->host_pm_state == IPC_MEM_HOST_PM_ACTIVE)
146 ipc_pm_signal_hpda_doorbell(ipc_pm, IPC_HP_PM_TRIGGER, true);
147}
148
149bool ipc_pm_trigger(struct iosm_pm *ipc_pm, enum ipc_pm_unit unit, bool active)
150{
151 union ipc_pm_cond old_cond;
152 union ipc_pm_cond new_cond;
153 bool link_active;
154
155
156 new_cond = ipc_pm->pm_cond;
157 old_cond = ipc_pm->pm_cond;
158
159
160 switch (unit) {
161 case IPC_PM_UNIT_IRQ:
162 new_cond.irq = active;
163 break;
164
165 case IPC_PM_UNIT_LINK:
166 new_cond.link = active;
167 break;
168
169 case IPC_PM_UNIT_HS:
170 new_cond.hs = active;
171 break;
172
173 default:
174 break;
175 }
176
177
178 if (old_cond.raw == new_cond.raw) {
179
180 link_active = old_cond.link == IPC_PM_ACTIVE;
181 goto ret;
182 }
183
184 ipc_pm->pm_cond = new_cond;
185
186 if (new_cond.link)
187 ipc_pm_on_link_wake(ipc_pm, unit == IPC_PM_UNIT_LINK);
188 else if (unit == IPC_PM_UNIT_LINK)
189 ipc_pm_on_link_sleep(ipc_pm);
190
191 if (old_cond.link == IPC_PM_SLEEP && new_cond.raw) {
192 link_active = ipc_pm_link_activate(ipc_pm);
193 goto ret;
194 }
195
196 link_active = old_cond.link == IPC_PM_ACTIVE;
197
198ret:
199 return link_active;
200}
201
202bool ipc_pm_prepare_host_sleep(struct iosm_pm *ipc_pm)
203{
204
205 if (ipc_pm->host_pm_state != IPC_MEM_HOST_PM_ACTIVE) {
206 dev_err(ipc_pm->dev, "host_pm_state=%d\tExpected to be: %d",
207 ipc_pm->host_pm_state, IPC_MEM_HOST_PM_ACTIVE);
208 return false;
209 }
210
211 ipc_pm->host_pm_state = IPC_MEM_HOST_PM_SLEEP_WAIT_D3;
212
213 return true;
214}
215
216bool ipc_pm_prepare_host_active(struct iosm_pm *ipc_pm)
217{
218 if (ipc_pm->host_pm_state != IPC_MEM_HOST_PM_SLEEP) {
219 dev_err(ipc_pm->dev, "host_pm_state=%d\tExpected to be: %d",
220 ipc_pm->host_pm_state, IPC_MEM_HOST_PM_SLEEP);
221 return false;
222 }
223
224
225 ipc_pm->host_pm_state = IPC_MEM_HOST_PM_ACTIVE_WAIT;
226
227 return true;
228}
229
230void ipc_pm_set_s2idle_sleep(struct iosm_pm *ipc_pm, bool sleep)
231{
232 if (sleep) {
233 ipc_pm->ap_state = IPC_MEM_DEV_PM_SLEEP;
234 ipc_pm->cp_state = IPC_MEM_DEV_PM_SLEEP;
235 ipc_pm->device_sleep_notification = IPC_MEM_DEV_PM_SLEEP;
236 } else {
237 ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
238 ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
239 ipc_pm->device_sleep_notification = IPC_MEM_DEV_PM_ACTIVE;
240 ipc_pm->pm_cond.link = IPC_PM_ACTIVE;
241 }
242}
243
244bool ipc_pm_dev_slp_notification(struct iosm_pm *ipc_pm, u32 cp_pm_req)
245{
246 if (cp_pm_req == ipc_pm->device_sleep_notification)
247 return false;
248
249 ipc_pm->device_sleep_notification = cp_pm_req;
250
251
252 switch (ipc_pm->cp_state) {
253 case IPC_MEM_DEV_PM_ACTIVE:
254 switch (cp_pm_req) {
255 case IPC_MEM_DEV_PM_ACTIVE:
256 break;
257
258 case IPC_MEM_DEV_PM_SLEEP:
259
260 ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_LINK, false);
261 return true;
262
263 default:
264 dev_err(ipc_pm->dev,
265 "loc-pm=%d active: confused req-pm=%d",
266 ipc_pm->cp_state, cp_pm_req);
267 break;
268 }
269 break;
270
271 case IPC_MEM_DEV_PM_SLEEP:
272 switch (cp_pm_req) {
273 case IPC_MEM_DEV_PM_ACTIVE:
274
275 ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_LINK, true);
276 break;
277
278 case IPC_MEM_DEV_PM_SLEEP:
279 break;
280
281 default:
282 dev_err(ipc_pm->dev,
283 "loc-pm=%d sleep: confused req-pm=%d",
284 ipc_pm->cp_state, cp_pm_req);
285 break;
286 }
287 break;
288
289 default:
290 dev_err(ipc_pm->dev, "confused loc-pm=%d, req-pm=%d",
291 ipc_pm->cp_state, cp_pm_req);
292 break;
293 }
294
295 return false;
296}
297
298void ipc_pm_init(struct iosm_protocol *ipc_protocol)
299{
300 struct iosm_imem *ipc_imem = ipc_protocol->imem;
301 struct iosm_pm *ipc_pm = &ipc_protocol->pm;
302
303 ipc_pm->pcie = ipc_imem->pcie;
304 ipc_pm->dev = ipc_imem->dev;
305
306 ipc_pm->pm_cond.irq = IPC_PM_SLEEP;
307 ipc_pm->pm_cond.hs = IPC_PM_SLEEP;
308 ipc_pm->pm_cond.link = IPC_PM_ACTIVE;
309
310 ipc_pm->cp_state = IPC_MEM_DEV_PM_ACTIVE;
311 ipc_pm->ap_state = IPC_MEM_DEV_PM_ACTIVE;
312 ipc_pm->host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
313
314
315
316
317 init_completion(&ipc_pm->host_sleep_complete);
318
319
320 smp_mb__before_atomic();
321
322 clear_bit(0, &ipc_pm->host_sleep_pend);
323
324
325 smp_mb__after_atomic();
326}
327
328void ipc_pm_deinit(struct iosm_protocol *proto)
329{
330 struct iosm_pm *ipc_pm = &proto->pm;
331
332 complete(&ipc_pm->host_sleep_complete);
333}
334