cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

iosm_ipc_pm.c (8442B)


      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}