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

pm.c (11030B)


      1// SPDX-License-Identifier: ISC
      2/*
      3 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
      4 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
      5 */
      6
      7#include "wil6210.h"
      8#include <linux/jiffies.h>
      9#include <linux/pm_runtime.h>
     10
     11#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
     12
     13static void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil)
     14{
     15	int i;
     16
     17	mutex_lock(&wil->vif_mutex);
     18	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
     19		struct wil6210_vif *vif = wil->vifs[i];
     20
     21		if (vif && test_bit(wil_vif_fwconnected, vif->status))
     22			wil_update_net_queues_bh(wil, vif, NULL, false);
     23	}
     24	mutex_unlock(&wil->vif_mutex);
     25}
     26
     27static void wil_pm_stop_all_net_queues(struct wil6210_priv *wil)
     28{
     29	int i;
     30
     31	mutex_lock(&wil->vif_mutex);
     32	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
     33		struct wil6210_vif *vif = wil->vifs[i];
     34
     35		if (vif)
     36			wil_update_net_queues_bh(wil, vif, NULL, true);
     37	}
     38	mutex_unlock(&wil->vif_mutex);
     39}
     40
     41static bool
     42wil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif,
     43		    bool is_runtime)
     44{
     45	struct wireless_dev *wdev = vif_to_wdev(vif);
     46
     47	switch (wdev->iftype) {
     48	case NL80211_IFTYPE_MONITOR:
     49		wil_dbg_pm(wil, "Sniffer\n");
     50		return false;
     51
     52	/* for STA-like interface, don't runtime suspend */
     53	case NL80211_IFTYPE_STATION:
     54	case NL80211_IFTYPE_P2P_CLIENT:
     55		if (test_bit(wil_vif_fwconnecting, vif->status)) {
     56			wil_dbg_pm(wil, "Delay suspend when connecting\n");
     57			return false;
     58		}
     59		if (is_runtime) {
     60			wil_dbg_pm(wil, "STA-like interface\n");
     61			return false;
     62		}
     63		break;
     64	/* AP-like interface - can't suspend */
     65	default:
     66		wil_dbg_pm(wil, "AP-like interface\n");
     67		return false;
     68	}
     69
     70	return true;
     71}
     72
     73int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
     74{
     75	int rc = 0, i;
     76	bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
     77				 wil->fw_capabilities);
     78	bool active_ifaces;
     79
     80	wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
     81
     82	if (wmi_only || debug_fw) {
     83		wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
     84			   wmi_only ? "wmi_only" : "debug_fw");
     85		rc = -EBUSY;
     86		goto out;
     87	}
     88	if (is_runtime && !wil->platform_ops.suspend) {
     89		rc = -EBUSY;
     90		goto out;
     91	}
     92
     93	mutex_lock(&wil->vif_mutex);
     94	active_ifaces = wil_has_active_ifaces(wil, true, false);
     95	mutex_unlock(&wil->vif_mutex);
     96
     97	if (!active_ifaces) {
     98		/* can always sleep when down */
     99		wil_dbg_pm(wil, "Interface is down\n");
    100		goto out;
    101	}
    102	if (test_bit(wil_status_resetting, wil->status)) {
    103		wil_dbg_pm(wil, "Delay suspend when resetting\n");
    104		rc = -EBUSY;
    105		goto out;
    106	}
    107	if (wil->recovery_state != fw_recovery_idle) {
    108		wil_dbg_pm(wil, "Delay suspend during recovery\n");
    109		rc = -EBUSY;
    110		goto out;
    111	}
    112
    113	/* interface is running */
    114	mutex_lock(&wil->vif_mutex);
    115	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
    116		struct wil6210_vif *vif = wil->vifs[i];
    117
    118		if (!vif)
    119			continue;
    120		if (!wil_can_suspend_vif(wil, vif, is_runtime)) {
    121			rc = -EBUSY;
    122			mutex_unlock(&wil->vif_mutex);
    123			goto out;
    124		}
    125	}
    126	mutex_unlock(&wil->vif_mutex);
    127
    128out:
    129	wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n",
    130		   is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc);
    131
    132	if (rc)
    133		wil->suspend_stats.rejected_by_host++;
    134
    135	return rc;
    136}
    137
    138static int wil_resume_keep_radio_on(struct wil6210_priv *wil)
    139{
    140	int rc = 0;
    141
    142	/* wil_status_resuming will be cleared when getting
    143	 * WMI_TRAFFIC_RESUME_EVENTID
    144	 */
    145	set_bit(wil_status_resuming, wil->status);
    146	clear_bit(wil_status_suspended, wil->status);
    147	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
    148	wil_unmask_irq(wil);
    149
    150	wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend);
    151
    152	/* Send WMI resume request to the device */
    153	rc = wmi_resume(wil);
    154	if (rc) {
    155		wil_err(wil, "device failed to resume (%d)\n", rc);
    156		if (no_fw_recovery)
    157			goto out;
    158		rc = wil_down(wil);
    159		if (rc) {
    160			wil_err(wil, "wil_down failed (%d)\n", rc);
    161			goto out;
    162		}
    163		rc = wil_up(wil);
    164		if (rc) {
    165			wil_err(wil, "wil_up failed (%d)\n", rc);
    166			goto out;
    167		}
    168	}
    169
    170	/* Wake all queues */
    171	wil_pm_wake_connected_net_queues(wil);
    172
    173out:
    174	if (rc)
    175		set_bit(wil_status_suspended, wil->status);
    176	return rc;
    177}
    178
    179static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
    180{
    181	int rc = 0;
    182	unsigned long data_comp_to;
    183
    184	wil_dbg_pm(wil, "suspend keep radio on\n");
    185
    186	/* Prevent handling of new tx and wmi commands */
    187	rc = down_write_trylock(&wil->mem_lock);
    188	if (!rc) {
    189		wil_err(wil,
    190			"device is busy. down_write_trylock failed, returned (0x%x)\n",
    191			rc);
    192		wil->suspend_stats.rejected_by_host++;
    193		return -EBUSY;
    194	}
    195
    196	set_bit(wil_status_suspending, wil->status);
    197	up_write(&wil->mem_lock);
    198
    199	wil_pm_stop_all_net_queues(wil);
    200
    201	if (!wil_is_tx_idle(wil)) {
    202		wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
    203		wil->suspend_stats.rejected_by_host++;
    204		goto reject_suspend;
    205	}
    206
    207	if (!wil->txrx_ops.is_rx_idle(wil)) {
    208		wil_dbg_pm(wil, "Pending RX data, reject suspend\n");
    209		wil->suspend_stats.rejected_by_host++;
    210		goto reject_suspend;
    211	}
    212
    213	if (!wil_is_wmi_idle(wil)) {
    214		wil_dbg_pm(wil, "Pending WMI events, reject suspend\n");
    215		wil->suspend_stats.rejected_by_host++;
    216		goto reject_suspend;
    217	}
    218
    219	/* Send WMI suspend request to the device */
    220	rc = wmi_suspend(wil);
    221	if (rc) {
    222		wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n",
    223			   rc);
    224		goto reject_suspend;
    225	}
    226
    227	/* Wait for completion of the pending RX packets */
    228	data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS);
    229	if (test_bit(wil_status_napi_en, wil->status)) {
    230		while (!wil->txrx_ops.is_rx_idle(wil)) {
    231			if (time_after(jiffies, data_comp_to)) {
    232				if (wil->txrx_ops.is_rx_idle(wil))
    233					break;
    234				wil_err(wil,
    235					"TO waiting for idle RX, suspend failed\n");
    236				wil->suspend_stats.r_on.failed_suspends++;
    237				goto resume_after_fail;
    238			}
    239			wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
    240			napi_synchronize(&wil->napi_rx);
    241			msleep(20);
    242		}
    243	}
    244
    245	/* In case of pending WMI events, reject the suspend
    246	 * and resume the device.
    247	 * This can happen if the device sent the WMI events before
    248	 * approving the suspend.
    249	 */
    250	if (!wil_is_wmi_idle(wil)) {
    251		wil_err(wil, "suspend failed due to pending WMI events\n");
    252		wil->suspend_stats.r_on.failed_suspends++;
    253		goto resume_after_fail;
    254	}
    255
    256	wil_mask_irq(wil);
    257
    258	/* Disable device reset on PERST */
    259	wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
    260
    261	if (wil->platform_ops.suspend) {
    262		rc = wil->platform_ops.suspend(wil->platform_handle, true);
    263		if (rc) {
    264			wil_err(wil, "platform device failed to suspend (%d)\n",
    265				rc);
    266			wil->suspend_stats.r_on.failed_suspends++;
    267			wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
    268			wil_unmask_irq(wil);
    269			goto resume_after_fail;
    270		}
    271	}
    272
    273	/* Save the current bus request to return to the same in resume */
    274	wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
    275	wil6210_bus_request(wil, 0);
    276
    277	set_bit(wil_status_suspended, wil->status);
    278	clear_bit(wil_status_suspending, wil->status);
    279
    280	return rc;
    281
    282resume_after_fail:
    283	set_bit(wil_status_resuming, wil->status);
    284	clear_bit(wil_status_suspending, wil->status);
    285	rc = wmi_resume(wil);
    286	/* if resume succeeded, reject the suspend */
    287	if (!rc) {
    288		rc = -EBUSY;
    289		wil_pm_wake_connected_net_queues(wil);
    290	}
    291	return rc;
    292
    293reject_suspend:
    294	clear_bit(wil_status_suspending, wil->status);
    295	wil_pm_wake_connected_net_queues(wil);
    296	return -EBUSY;
    297}
    298
    299static int wil_suspend_radio_off(struct wil6210_priv *wil)
    300{
    301	int rc = 0;
    302	bool active_ifaces;
    303
    304	wil_dbg_pm(wil, "suspend radio off\n");
    305
    306	rc = down_write_trylock(&wil->mem_lock);
    307	if (!rc) {
    308		wil_err(wil,
    309			"device is busy. down_write_trylock failed, returned (0x%x)\n",
    310			rc);
    311		wil->suspend_stats.rejected_by_host++;
    312		return -EBUSY;
    313	}
    314
    315	set_bit(wil_status_suspending, wil->status);
    316	up_write(&wil->mem_lock);
    317
    318	/* if netif up, hardware is alive, shut it down */
    319	mutex_lock(&wil->vif_mutex);
    320	active_ifaces = wil_has_active_ifaces(wil, true, false);
    321	mutex_unlock(&wil->vif_mutex);
    322
    323	if (active_ifaces) {
    324		rc = wil_down(wil);
    325		if (rc) {
    326			wil_err(wil, "wil_down : %d\n", rc);
    327			wil->suspend_stats.r_off.failed_suspends++;
    328			goto out;
    329		}
    330	}
    331
    332	/* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
    333	wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
    334	wil_disable_irq(wil);
    335
    336	if (wil->platform_ops.suspend) {
    337		rc = wil->platform_ops.suspend(wil->platform_handle, false);
    338		if (rc) {
    339			wil_enable_irq(wil);
    340			wil->suspend_stats.r_off.failed_suspends++;
    341			goto out;
    342		}
    343	}
    344
    345	set_bit(wil_status_suspended, wil->status);
    346
    347out:
    348	clear_bit(wil_status_suspending, wil->status);
    349	wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
    350
    351	return rc;
    352}
    353
    354static int wil_resume_radio_off(struct wil6210_priv *wil)
    355{
    356	int rc = 0;
    357	bool active_ifaces;
    358
    359	wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
    360	wil_enable_irq(wil);
    361	/* if any netif up, bring hardware up
    362	 * During open(), IFF_UP set after actual device method
    363	 * invocation. This prevent recursive call to wil_up()
    364	 * wil_status_suspended will be cleared in wil_reset
    365	 */
    366	mutex_lock(&wil->vif_mutex);
    367	active_ifaces = wil_has_active_ifaces(wil, true, false);
    368	mutex_unlock(&wil->vif_mutex);
    369	if (active_ifaces)
    370		rc = wil_up(wil);
    371	else
    372		clear_bit(wil_status_suspended, wil->status);
    373
    374	return rc;
    375}
    376
    377int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
    378{
    379	int rc = 0;
    380
    381	wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
    382
    383	if (test_bit(wil_status_suspended, wil->status)) {
    384		wil_dbg_pm(wil, "trying to suspend while suspended\n");
    385		return 0;
    386	}
    387
    388	if (!keep_radio_on)
    389		rc = wil_suspend_radio_off(wil);
    390	else
    391		rc = wil_suspend_keep_radio_on(wil);
    392
    393	wil_dbg_pm(wil, "suspend: %s => %d\n",
    394		   is_runtime ? "runtime" : "system", rc);
    395
    396	return rc;
    397}
    398
    399int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
    400{
    401	int rc = 0;
    402
    403	wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
    404
    405	if (wil->platform_ops.resume) {
    406		rc = wil->platform_ops.resume(wil->platform_handle,
    407					      keep_radio_on);
    408		if (rc) {
    409			wil_err(wil, "platform_ops.resume : %d\n", rc);
    410			goto out;
    411		}
    412	}
    413
    414	if (keep_radio_on)
    415		rc = wil_resume_keep_radio_on(wil);
    416	else
    417		rc = wil_resume_radio_off(wil);
    418
    419out:
    420	wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
    421		   rc);
    422	return rc;
    423}
    424
    425void wil_pm_runtime_allow(struct wil6210_priv *wil)
    426{
    427	struct device *dev = wil_to_dev(wil);
    428
    429	pm_runtime_put_noidle(dev);
    430	pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
    431	pm_runtime_use_autosuspend(dev);
    432	pm_runtime_allow(dev);
    433}
    434
    435void wil_pm_runtime_forbid(struct wil6210_priv *wil)
    436{
    437	struct device *dev = wil_to_dev(wil);
    438
    439	pm_runtime_forbid(dev);
    440	pm_runtime_get_noresume(dev);
    441}
    442
    443int wil_pm_runtime_get(struct wil6210_priv *wil)
    444{
    445	int rc;
    446	struct device *dev = wil_to_dev(wil);
    447
    448	rc = pm_runtime_resume_and_get(dev);
    449	if (rc < 0) {
    450		wil_err(wil, "pm_runtime_resume_and_get() failed, rc = %d\n", rc);
    451		return rc;
    452	}
    453
    454	return 0;
    455}
    456
    457void wil_pm_runtime_put(struct wil6210_priv *wil)
    458{
    459	struct device *dev = wil_to_dev(wil);
    460
    461	pm_runtime_mark_last_busy(dev);
    462	pm_runtime_put_autosuspend(dev);
    463}