Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eac11766 authored by Maya Erez's avatar Maya Erez Committed by Lior David
Browse files

wil6210: add support for pci linkdown recovery



Some platforms can notify on pci linkdown events.
Add pci linkdown recovery flow, invoked upon pci linkdown
notification.
A new wil6210 status flag is added, wil_status_pci_linkdown, to
prevent pci suspend / resume until the recovery is completed.
After the pci link is recovered in the platform pci recovery
callback the device is being reset and the the interface and AP
mode states are restored.

Change-Id: I6e0f434d52704120c683c51fb4d664eccca16f01
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
[liord@codeaurora.org: fix merge conflicts]
Signed-off-by: default avatarLior David <liord@codeaurora.org>
parent 0da385b3
Loading
Loading
Loading
Loading
+22 −9
Original line number Diff line number Diff line
@@ -525,22 +525,16 @@ bool wil_is_recovery_blocked(struct wil6210_priv *wil)
	return no_fw_recovery && (wil->recovery_state == fw_recovery_pending);
}

static void wil_fw_error_worker(struct work_struct *work)
void wil_fw_recovery(struct wil6210_priv *wil)
{
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						fw_error_worker);
	struct net_device *ndev = wil->main_ndev;
	struct wireless_dev *wdev;

	wil_dbg_misc(wil, "fw error worker\n");
	wil_dbg_misc(wil, "fw recovery\n");

	if (!ndev || !(ndev->flags & IFF_UP)) {
		wil_info(wil, "No recovery - interface is down\n");
		return;
	}
	wdev = ndev->ieee80211_ptr;

	/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
	/* increment @recovery_count if less than WIL6210_FW_RECOVERY_TO
	 * passed since last recovery attempt
	 */
	if (time_is_after_jiffies(wil->last_fw_recovery +
@@ -600,6 +594,22 @@ static void wil_fw_error_worker(struct work_struct *work)
	rtnl_unlock();
}

static void wil_fw_error_worker(struct work_struct *work)
{
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						fw_error_worker);
	struct net_device *ndev = wil->main_ndev;

	wil_dbg_misc(wil, "fw error worker\n");

	if (!ndev || !(ndev->flags & IFF_UP)) {
		wil_info(wil, "No recovery - interface is down\n");
		return;
	}

	wil_fw_recovery(wil);
}

static int wil_find_free_ring(struct wil6210_priv *wil)
{
	int i;
@@ -712,6 +722,8 @@ int wil_priv_init(struct wil6210_priv *wil)

	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
	INIT_WORK(&wil->pci_linkdown_recovery_worker,
		  wil_pci_linkdown_recovery_worker);

	INIT_LIST_HEAD(&wil->pending_wmi_ev);
	spin_lock_init(&wil->wmi_ev_lock);
@@ -827,6 +839,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)

	wil_set_recovery_state(wil, fw_recovery_idle);
	cancel_work_sync(&wil->fw_error_worker);
	cancel_work_sync(&wil->pci_linkdown_recovery_worker);
	wmi_event_flush(wil);
	destroy_workqueue(wil->wq_service);
	destroy_workqueue(wil->wmi_wq);
+108 −0
Original line number Diff line number Diff line
@@ -283,6 +283,108 @@ static int wil_platform_rop_fw_recovery(void *wil_handle)
	return 0;
}

void wil_pci_linkdown_recovery_worker(struct work_struct *work)
{
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						pci_linkdown_recovery_worker);
	int rc, i;
	struct wil6210_vif *vif;
	struct net_device *ndev = wil->main_ndev;

	wil_dbg_misc(wil, "starting pci_linkdown recovery\n");

	rtnl_lock();
	mutex_lock(&wil->mutex);
	down_write(&wil->mem_lock);
	clear_bit(wil_status_fwready, wil->status);
	set_bit(wil_status_pci_linkdown, wil->status);
	set_bit(wil_status_resetting, wil->status);
	up_write(&wil->mem_lock);

	if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
		napi_disable(&wil->napi_rx);
		napi_disable(&wil->napi_tx);
	}

	mutex_unlock(&wil->mutex);
	rtnl_unlock();

	mutex_lock(&wil->mutex);

	mutex_lock(&wil->vif_mutex);
	wil_ftm_stop_operations(wil);
	wil_p2p_stop_radio_operations(wil);
	wil_abort_scan_all_vifs(wil, false);
	mutex_unlock(&wil->vif_mutex);

	for (i = 0; i < wil->max_vifs; i++) {
		vif = wil->vifs[i];
		if (vif) {
			cancel_work_sync(&vif->disconnect_worker);
			wil6210_disconnect(vif, NULL,
					   WLAN_REASON_DEAUTH_LEAVING);
		}
	}

	wmi_event_flush(wil);
	flush_workqueue(wil->wq_service);
	flush_workqueue(wil->wmi_wq);

	/* Recover PCIe */
	if (wil->platform_ops.pci_linkdown_recovery) {
		rc = wil->platform_ops.pci_linkdown_recovery(
			wil->platform_handle);
		if (rc) {
			wil_err(wil,
				"platform device failed to recover from pci linkdown (%d)\n",
				rc);
			mutex_unlock(&wil->mutex);
			goto out;
		}
	} else {
		wil_err(wil,
			"platform device doesn't support pci_linkdown recovery\n");
		mutex_unlock(&wil->mutex);
		goto out;
	}

	if (!ndev || !(ndev->flags & IFF_UP)) {
		wil_reset(wil, false);
		mutex_unlock(&wil->mutex);
	} else {
		mutex_unlock(&wil->mutex);
		wil->recovery_state = fw_recovery_pending;
		wil_fw_recovery(wil);
	}

out:
	return;
}

static int wil_platform_rop_notify(void *wil_handle,
				   enum wil_platform_notif notif)
{
	struct wil6210_priv *wil = wil_handle;

	if (!wil)
		return -EINVAL;

	switch (notif) {
	case WIL_PLATFORM_NOTIF_PCI_LINKDOWN:
		wil_info(wil, "received WIL_PLATFORM_NOTIF_PCI_LINKDOWN\n");
		clear_bit(wil_status_fwready, wil->status);
		set_bit(wil_status_resetting, wil->status);
		set_bit(wil_status_pci_linkdown, wil->status);

		schedule_work(&wil->pci_linkdown_recovery_worker);
		break;
	default:
		break;
	}

	return 0;
}

static void wil_platform_ops_uninit(struct wil6210_priv *wil)
{
	if (wil->platform_ops.uninit)
@@ -298,6 +400,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	const struct wil_platform_rops rops = {
		.ramdump = wil_platform_rop_ramdump,
		.fw_recovery = wil_platform_rop_fw_recovery,
		.notify = wil_platform_rop_notify,
	};
	u32 bar_size = pci_resource_len(pdev, 0);
	int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */
@@ -536,6 +639,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
	struct wil6210_priv *wil = pci_get_drvdata(pdev);
	bool keep_radio_on, active_ifaces;

	if (test_bit(wil_status_pci_linkdown, wil->status)) {
		wil_dbg_pm(wil, "ignore resume during pci linkdown\n");
		return 0;
	}

	wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");

	mutex_lock(&wil->vif_mutex);
+6 −0
Original line number Diff line number Diff line
@@ -90,6 +90,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
		goto out;
	}

	if (test_bit(wil_status_pci_linkdown, wil->status)) {
		wil_dbg_pm(wil, "Delay suspend during pci linkdown\n");
		rc = -EBUSY;
		goto out;
	}

	mutex_lock(&wil->vif_mutex);
	active_ifaces = wil_has_active_ifaces(wil, true, false);
	mutex_unlock(&wil->vif_mutex);
+6 −0
Original line number Diff line number Diff line
@@ -651,6 +651,7 @@ enum { /* for wil6210_priv.status */
	wil_status_suspending, /* suspend in progress */
	wil_status_suspended, /* suspend completed, device is suspended */
	wil_status_resuming, /* resume in progress */
	wil_status_pci_linkdown, /* pci linkdown occurred */
	wil_status_last /* keep last */
};

@@ -1059,6 +1060,8 @@ struct wil6210_priv {

	u32 max_agg_wsize;
	u32 max_ampdu_size;

	struct work_struct pci_linkdown_recovery_worker;
};

#define wil_to_wiphy(i) (i->wiphy)
@@ -1350,6 +1353,9 @@ void wil_disconnect_worker(struct work_struct *work);

void wil_init_txrx_ops(struct wil6210_priv *wil);

void wil_fw_recovery(struct wil6210_priv *wil);
void wil_pci_linkdown_recovery_worker(struct work_struct *work);

/* TX API */
int wil_ring_init_tx(struct wil6210_vif *vif, int cid);
int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size);
+10 −12
Original line number Diff line number Diff line
/* SPDX-License-Identifier: ISC */
/*
 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 */

#ifndef __WIL_PLATFORM_H__
@@ -27,6 +17,10 @@ enum wil_platform_event {
	WIL_PLATFORM_EVT_POST_SUSPEND = 4,
};

enum wil_platform_notif {
	WIL_PLATFORM_NOTIF_PCI_LINKDOWN = 0,
};

enum wil_platform_features {
	WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0,
	WIL_PLATFORM_FEATURE_TRIPLE_MSI = 1,
@@ -52,6 +46,7 @@ struct wil_platform_ops {
	int (*notify)(void *handle, enum wil_platform_event evt);
	int (*get_capa)(void *handle);
	void (*set_features)(void *handle, int features);
	int (*pci_linkdown_recovery)(void *handle);
};

/**
@@ -63,10 +58,13 @@ struct wil_platform_ops {
 * @fw_recovery: start a firmware recovery process. Called as
 *      part of a crash recovery process which may include other
 *      related platform subsystems.
 * @notify: get notifications from the Platform driver, such as
 *      pci linkdown
 */
struct wil_platform_rops {
	int (*ramdump)(void *wil_handle, void *buf, uint32_t size);
	int (*fw_recovery)(void *wil_handle);
	int (*notify)(void *wil_handle, enum wil_platform_notif notif);
};

/**