brintos

brintos / linux-shallow public Read only

0
0
Text · 10.8 KiB · f521af5 Raw
464 lines · c
1// SPDX-License-Identifier: ISC2/*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 bool42wil_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 getting143	 * WMI_TRAFFIC_RESUME_EVENTID144	 */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 suspend246	 * and resume the device.247	 * This can happen if the device sent the WMI events before248	 * 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 up362	 * During open(), IFF_UP set after actual device method363	 * invocation. This prevent recursive call to wil_up()364	 * wil_status_suspended will be cleared in wil_reset365	 */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	else372		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	else391		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	else417		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}464