GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / net / wwan / iosm / iosm_ipc_pm.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2020-21 Intel Corporation.
4  */
5
6 #include "iosm_ipc_protocol.h"
7
8 /* Timeout value in MS for the PM to wait for device to reach active state */
9 #define IPC_PM_ACTIVE_TIMEOUT_MS (500)
10
11 /* Note that here "active" has the value 1, as compared to the enums
12  * ipc_mem_host_pm_state or ipc_mem_dev_pm_state, where "active" is 0
13  */
14 #define IPC_PM_SLEEP (0)
15 #define CONSUME_STATE (0)
16 #define IPC_PM_ACTIVE (1)
17
18 void 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         /* Trigger the irq towards CP */
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 /* Wake up the device if it is in low power mode. */
45 static 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                         /* Wake up the device. */
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
66 not_active:
67         /* link is not ready */
68         return false;
69 }
70
71 bool 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                 /* Complete all memory stores before setting bit */
77                 smp_mb__before_atomic();
78
79                 /* Wait for IPC_PM_ACTIVE_TIMEOUT_MS for Device sleep state
80                  * machine to enter ACTIVE state.
81                  */
82                 set_bit(0, &ipc_pm->host_sleep_pend);
83
84                 /* Complete all memory stores after setting bit */
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;
98 active_timeout:
99         /* Complete all memory stores before clearing bit */
100         smp_mb__before_atomic();
101
102         /* Reset the atomic variable in any case as device sleep
103          * state machine change is no longer of interest.
104          */
105         clear_bit(0, &ipc_pm->host_sleep_pend);
106
107         /* Complete all memory stores after clearing bit */
108         smp_mb__after_atomic();
109
110         return ret_val;
111 }
112
113 static void ipc_pm_on_link_sleep(struct iosm_pm *ipc_pm)
114 {
115         /* pending sleep ack and all conditions are cleared
116          * -> signal SLEEP__ACK to CP
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
124 static 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                 /* check the consume state !!! */
134                 if (test_bit(CONSUME_STATE, &ipc_pm->host_sleep_pend))
135                         complete(&ipc_pm->host_sleep_complete);
136         }
137
138         /* Check for pending HPDA update.
139          * Pending HP update could be because of sending message was
140          * put on hold due to Device sleep state or due to TD update
141          * which could be because of Device Sleep and Host Sleep
142          * states.
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
149 bool 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         /* Save the current D3 state. */
156         new_cond = ipc_pm->pm_cond;
157         old_cond = ipc_pm->pm_cond;
158
159         /* Calculate the power state only in the runtime phase. */
160         switch (unit) {
161         case IPC_PM_UNIT_IRQ: /* CP irq */
162                 new_cond.irq = active;
163                 break;
164
165         case IPC_PM_UNIT_LINK: /* Device link state. */
166                 new_cond.link = active;
167                 break;
168
169         case IPC_PM_UNIT_HS: /* Host sleep trigger requires Link. */
170                 new_cond.hs = active;
171                 break;
172
173         default:
174                 break;
175         }
176
177         /* Something changed ? */
178         if (old_cond.raw == new_cond.raw) {
179                 /* Stay in the current PM state. */
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
198 ret:
199         return link_active;
200 }
201
202 bool ipc_pm_prepare_host_sleep(struct iosm_pm *ipc_pm)
203 {
204         /* suspend not allowed if host_pm_state is not IPC_MEM_HOST_PM_ACTIVE */
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
216 bool 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         /* Sending Sleep Exit message to CP. Update the state */
225         ipc_pm->host_pm_state = IPC_MEM_HOST_PM_ACTIVE_WAIT;
226
227         return true;
228 }
229
230 void 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
244 bool 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         /* Evaluate the PM request. */
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                         /* Inform the PM that the device link can go down. */
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                         /* Inform the PM that the device link is active. */
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
298 void 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         /* Create generic wait-for-completion handler for Host Sleep
315          * and device sleep coordination.
316          */
317         init_completion(&ipc_pm->host_sleep_complete);
318
319         /* Complete all memory stores before clearing bit */
320         smp_mb__before_atomic();
321
322         clear_bit(0, &ipc_pm->host_sleep_pend);
323
324         /* Complete all memory stores after clearing bit */
325         smp_mb__after_atomic();
326 }
327
328 void 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 }