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
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	/* 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. */
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			/* 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
66not_active:
67	/* link is not ready */
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		/* 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;
98active_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
113static 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
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		/* 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
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	/* 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
198ret:
199	return link_active;
200}
201
202bool 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
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	/* 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
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	/* 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
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	/* 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
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