• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ipc_pm_signal_hpda_doorbell(struct iosm_pm * ipc_pm,u32 identifier,bool host_slp_check)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. */
ipc_pm_link_activate(struct iosm_pm * ipc_pm)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 
ipc_pm_wait_for_device_active(struct iosm_pm * ipc_pm)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 
ipc_pm_on_link_sleep(struct iosm_pm * ipc_pm)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 
ipc_pm_on_link_wake(struct iosm_pm * ipc_pm,bool ack)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 
ipc_pm_trigger(struct iosm_pm * ipc_pm,enum ipc_pm_unit unit,bool active)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 
ipc_pm_prepare_host_sleep(struct iosm_pm * ipc_pm)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 
ipc_pm_prepare_host_active(struct iosm_pm * ipc_pm)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 
ipc_pm_set_s2idle_sleep(struct iosm_pm * ipc_pm,bool sleep)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 
ipc_pm_dev_slp_notification(struct iosm_pm * ipc_pm,u32 cp_pm_req)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 
ipc_pm_init(struct iosm_protocol * ipc_protocol)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 
ipc_pm_deinit(struct iosm_protocol * proto)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 }
334