main.c 31.7 KB
Newer Older
1
/*
2
 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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.
 */

#include <linux/moduleparam.h>
#include <linux/if_arp.h>
19
#include <linux/etherdevice.h>
20 21

#include "wil6210.h"
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
22
#include "txrx.h"
23
#include "wmi.h"
24
#include "boot_loader.h"
25

26
#define WAIT_FOR_HALP_VOTE_MS 100
27
#define WAIT_FOR_SCAN_ABORT_MS 1000
28

29
bool debug_fw; /* = false; */
30
module_param(debug_fw, bool, 0444);
31 32
MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");

33
static bool oob_mode;
34
module_param(oob_mode, bool, 0444);
35 36 37
MODULE_PARM_DESC(oob_mode,
		 " enable out of the box (OOB) mode in FW, for diagnostics and certification");

38
bool no_fw_recovery;
39
module_param(no_fw_recovery, bool, 0644);
40
MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
41

42 43 44 45
/* if not set via modparam, will be set to default value of 1/8 of
 * rx ring size during init flow
 */
unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT;
46
module_param(rx_ring_overflow_thrsh, ushort, 0444);
47 48
MODULE_PARM_DESC(rx_ring_overflow_thrsh,
		 " RX ring overflow threshold in descriptors.");
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
49

50 51 52
/* We allow allocation of more than 1 page buffers to support large packets.
 * It is suboptimal behavior performance wise in case MTU above page size.
 */
53
unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD;
54 55 56 57 58 59 60 61 62 63 64
static int mtu_max_set(const char *val, const struct kernel_param *kp)
{
	int ret;

	/* sets mtu_max directly. no need to restore it in case of
	 * illegal value since we assume this will fail insmod
	 */
	ret = param_set_uint(val, kp);
	if (ret)
		return ret;

65
	if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU)
66 67 68 69 70
		ret = -EINVAL;

	return ret;
}

71
static const struct kernel_param_ops mtu_max_ops = {
72 73 74 75
	.set = mtu_max_set,
	.get = param_get_uint,
};

76
module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, 0444);
77 78
MODULE_PARM_DESC(mtu_max, " Max MTU value.");

79 80
static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
81
static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

static int ring_order_set(const char *val, const struct kernel_param *kp)
{
	int ret;
	uint x;

	ret = kstrtouint(val, 0, &x);
	if (ret)
		return ret;

	if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
		return -EINVAL;

	*((uint *)kp->arg) = x;

	return 0;
}

100
static const struct kernel_param_ops ring_order_ops = {
101 102 103 104
	.set = ring_order_set,
	.get = param_get_uint,
};

105
module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, 0444);
106
MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
107
module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, 0444);
108
MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
109
module_param_cb(bcast_ring_order, &ring_order_ops, &bcast_ring_order, 0444);
110
MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order");
111

112 113 114
#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
/*
 * Due to a hardware issue,
 * one has to read/write to/from NIC in 32-bit chunks;
 * regular memcpy_fromio and siblings will
 * not work on 64-bit platform - it uses 64-bit transactions
 *
 * Force 32-bit transactions to enable NIC on 64-bit platforms
 *
 * To avoid byte swap on big endian host, __raw_{read|write}l
 * should be used - {read|write}l would swap bytes to provide
 * little endian on PCI value in host endianness.
 */
void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
			  size_t count)
{
	u32 *d = dst;
	const volatile u32 __iomem *s = src;

	/* size_t is unsigned, if (count%4 != 0) it will wrap */
	for (count += 4; count > 4; count -= 4)
		*d++ = __raw_readl(s++);
}

138 139 140 141 142 143 144 145
void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst,
				 const volatile void __iomem *src, size_t count)
{
	wil_halp_vote(wil);
	wil_memcpy_fromio_32(dst, src, count);
	wil_halp_unvote(wil);
}

146 147 148 149 150 151 152 153 154 155
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
			size_t count)
{
	volatile u32 __iomem *d = dst;
	const u32 *s = src;

	for (count += 4; count > 4; count -= 4)
		__raw_writel(*s++, d++);
}

156 157 158 159 160 161 162 163 164
void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil,
			       volatile void __iomem *dst,
			       const void *src, size_t count)
{
	wil_halp_vote(wil);
	wil_memcpy_toio_32(dst, src, count);
	wil_halp_unvote(wil);
}

165
static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
166
			       u16 reason_code, bool from_event)
167
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
168 169
{
	uint i;
170 171
	struct net_device *ndev = wil_to_ndev(wil);
	struct wireless_dev *wdev = wil->wdev;
172
	struct wil_sta_info *sta = &wil->sta[cid];
173

174
	might_sleep();
175 176
	wil_dbg_misc(wil, "disconnect_cid: CID %d, status %d\n",
		     cid, sta->status);
177
	/* inform upper/lower layers */
178
	if (sta->status != wil_sta_unused) {
179 180 181 182 183 184
		if (!from_event) {
			bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
						disable_ap_sme : false;
			wmi_disconnect_sta(wil, sta->addr, reason_code,
					   true, del_sta);
		}
185

186 187 188 189 190 191 192 193 194
		switch (wdev->iftype) {
		case NL80211_IFTYPE_AP:
		case NL80211_IFTYPE_P2P_GO:
			/* AP-like interface */
			cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
			break;
		default:
			break;
		}
195 196
		sta->status = wil_sta_unused;
	}
197
	/* reorder buffers */
198
	for (i = 0; i < WIL_STA_TID_NUM; i++) {
199 200
		struct wil_tid_ampdu_rx *r;

201
		spin_lock_bh(&sta->tid_rx_lock);
202 203

		r = sta->tid_rx[i];
204 205
		sta->tid_rx[i] = NULL;
		wil_tid_ampdu_rx_free(wil, r);
206

207
		spin_unlock_bh(&sta->tid_rx_lock);
208
	}
209 210 211 212
	/* crypto context */
	memset(sta->tid_crypto_rx, 0, sizeof(sta->tid_crypto_rx));
	memset(&sta->group_crypto_rx, 0, sizeof(sta->group_crypto_rx));
	/* release vrings */
213 214 215 216
	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
		if (wil->vring2cid_tid[i][0] == cid)
			wil_vring_fini_tx(wil, i);
	}
217
	/* statistics */
218 219 220
	memset(&sta->stats, 0, sizeof(sta->stats));
}

221
static bool wil_is_connected(struct wil6210_priv *wil)
222 223 224 225 226 227 228 229 230 231 232
{
	int i;

	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		if (wil->sta[i].status == wil_sta_connected)
			return true;
	}

	return false;
}

233
static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
234
				u16 reason_code, bool from_event)
235
{
236
	int cid = -ENOENT;
237
	struct net_device *ndev = wil_to_ndev(wil);
238 239
	struct wireless_dev *wdev = wil->wdev;

240 241 242
	if (unlikely(!ndev))
		return;

243
	might_sleep();
244
	wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
245
		 reason_code, from_event ? "+" : "-");
246 247 248 249 250 251

	/* Cases are:
	 * - disconnect single STA, still connected
	 * - disconnect single STA, already disconnected
	 * - disconnect all
	 *
252
	 * For "disconnect all", there are 3 options:
253
	 * - bssid == NULL
254
	 * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
255 256
	 * - bssid is our MAC address
	 */
257 258
	if (bssid && !is_broadcast_ether_addr(bssid) &&
	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
259
		cid = wil_find_cid(wil, bssid);
260 261 262 263 264 265
		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
			     bssid, cid, reason_code);
		if (cid >= 0) /* disconnect 1 peer */
			wil_disconnect_cid(wil, cid, reason_code, from_event);
	} else { /* all */
		wil_dbg_misc(wil, "Disconnect all\n");
266
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
267
			wil_disconnect_cid(wil, cid, reason_code, from_event);
268
	}
269 270 271 272 273

	/* link state */
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
274
		wil_bcast_fini(wil);
275
		wil_update_net_queues_bh(wil, NULL, true);
276
		netif_carrier_off(ndev);
277
		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
278

279 280
		if (test_bit(wil_status_fwconnected, wil->status)) {
			clear_bit(wil_status_fwconnected, wil->status);
281
			cfg80211_disconnected(ndev, reason_code,
282
					      NULL, 0, false, GFP_KERNEL);
283
		} else if (test_bit(wil_status_fwconnecting, wil->status)) {
284 285 286
			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
						WLAN_STATUS_UNSPECIFIED_FAILURE,
						GFP_KERNEL);
287
			wil->bss = NULL;
288
		}
289
		clear_bit(wil_status_fwconnecting, wil->status);
290
		break;
291 292
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
293 294
		if (!wil_is_connected(wil)) {
			wil_update_net_queues_bh(wil, NULL, true);
295
			clear_bit(wil_status_fwconnected, wil->status);
296 297 298
		} else {
			wil_update_net_queues_bh(wil, NULL, false);
		}
299
		break;
300 301
	default:
		break;
302 303 304 305 306 307 308
	}
}

static void wil_disconnect_worker(struct work_struct *work)
{
	struct wil6210_priv *wil = container_of(work,
			struct wil6210_priv, disconnect_worker);
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
	struct net_device *ndev = wil_to_ndev(wil);
	int rc;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_disconnect_event evt;
	} __packed reply;

	if (test_bit(wil_status_fwconnected, wil->status))
		/* connect succeeded after all */
		return;

	if (!test_bit(wil_status_fwconnecting, wil->status))
		/* already disconnected */
		return;

	rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
		      WMI_DISCONNECT_EVENTID, &reply, sizeof(reply),
		      WIL6210_DISCONNECT_TO_MS);
	if (rc) {
		wil_err(wil, "disconnect error %d\n", rc);
		return;
	}
331

332 333 334 335 336
	wil_update_net_queues_bh(wil, NULL, true);
	netif_carrier_off(ndev);
	cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0,
				WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
	clear_bit(wil_status_fwconnecting, wil->status);
337 338 339 340 341
}

static void wil_connect_timer_fn(ulong x)
{
	struct wil6210_priv *wil = (void *)x;
342
	bool q;
343

344
	wil_err(wil, "Connect timeout detected, disconnect station\n");
345 346

	/* reschedule to thread context - disconnect won't
347 348
	 * run from atomic context.
	 * queue on wmi_wq to prevent race with connect event.
349
	 */
350 351
	q = queue_work(wil->wmi_wq, &wil->disconnect_worker);
	wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
352 353
}

354 355 356 357
static void wil_scan_timer_fn(ulong x)
{
	struct wil6210_priv *wil = (void *)x;

358
	clear_bit(wil_status_fwready, wil->status);
359
	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
360
	wil_fw_error_recovery(wil);
361 362
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
static int wil_wait_for_recovery(struct wil6210_priv *wil)
{
	if (wait_event_interruptible(wil->wq, wil->recovery_state !=
				     fw_recovery_pending)) {
		wil_err(wil, "Interrupt, canceling recovery\n");
		return -ERESTARTSYS;
	}
	if (wil->recovery_state != fw_recovery_running) {
		wil_info(wil, "Recovery cancelled\n");
		return -EINTR;
	}
	wil_info(wil, "Proceed with recovery\n");
	return 0;
}

void wil_set_recovery_state(struct wil6210_priv *wil, int state)
{
380
	wil_dbg_misc(wil, "set_recovery_state: %d -> %d\n",
381 382 383 384 385 386
		     wil->recovery_state, state);

	wil->recovery_state = state;
	wake_up_interruptible(&wil->wq);
}

387 388 389 390 391
bool wil_is_recovery_blocked(struct wil6210_priv *wil)
{
	return no_fw_recovery && (wil->recovery_state == fw_recovery_pending);
}

392 393
static void wil_fw_error_worker(struct work_struct *work)
{
394 395
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						fw_error_worker);
396 397 398 399
	struct wireless_dev *wdev = wil->wdev;

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

400 401 402 403 404
	if (!netif_running(wil_to_ndev(wil))) {
		wil_info(wil, "No recovery - interface is down\n");
		return;
	}

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
	/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
	 * passed since last recovery attempt
	 */
	if (time_is_after_jiffies(wil->last_fw_recovery +
				  WIL6210_FW_RECOVERY_TO))
		wil->recovery_count++;
	else
		wil->recovery_count = 1; /* fw was alive for a long time */

	if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
		wil_err(wil, "too many recovery attempts (%d), giving up\n",
			wil->recovery_count);
		return;
	}

	wil->last_fw_recovery = jiffies;

422 423 424 425 426 427 428
	wil_info(wil, "fw error recovery requested (try %d)...\n",
		 wil->recovery_count);
	if (!no_fw_recovery)
		wil->recovery_state = fw_recovery_running;
	if (wil_wait_for_recovery(wil) != 0)
		return;

429
	mutex_lock(&wil->mutex);
430 431 432 433
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
	case NL80211_IFTYPE_MONITOR:
434
		/* silent recovery, upper layers will see disconnect */
435 436
		__wil_down(wil);
		__wil_up(wil);
437 438 439
		break;
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
440
		wil_info(wil, "No recovery for AP-like interface\n");
441 442 443
		/* recovery in these modes is done by upper layers */
		break;
	default:
444 445
		wil_err(wil, "No recovery - unknown interface type %d\n",
			wdev->iftype);
446 447
		break;
	}
448
	mutex_unlock(&wil->mutex);
449 450
}

451 452 453
static int wil_find_free_vring(struct wil6210_priv *wil)
{
	int i;
454

455 456 457 458 459 460 461
	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
		if (!wil->vring_tx[i].va)
			return i;
	}
	return -EINVAL;
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
int wil_tx_init(struct wil6210_priv *wil, int cid)
{
	int rc = -EINVAL, ringid;

	if (cid < 0) {
		wil_err(wil, "No connection pending\n");
		goto out;
	}
	ringid = wil_find_free_vring(wil);
	if (ringid < 0) {
		wil_err(wil, "No free vring found\n");
		goto out;
	}

	wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
		    cid, ringid);

	rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
	if (rc)
		wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n",
			cid, ringid);

out:
	return rc;
}

488 489 490 491 492 493 494 495 496 497 498
int wil_bcast_init(struct wil6210_priv *wil)
{
	int ri = wil->bcast_vring, rc;

	if ((ri >= 0) && wil->vring_tx[ri].va)
		return 0;

	ri = wil_find_free_vring(wil);
	if (ri < 0)
		return ri;

499
	wil->bcast_vring = ri;
500
	rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
501 502
	if (rc)
		wil->bcast_vring = -1;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

	return rc;
}

void wil_bcast_fini(struct wil6210_priv *wil)
{
	int ri = wil->bcast_vring;

	if (ri < 0)
		return;

	wil->bcast_vring = -1;
	wil_vring_fini_tx(wil, ri);
}

518 519
int wil_priv_init(struct wil6210_priv *wil)
{
520 521
	uint i;

522
	wil_dbg_misc(wil, "priv_init\n");
523

524
	memset(wil->sta, 0, sizeof(wil->sta));
525 526
	for (i = 0; i < WIL6210_MAX_CID; i++)
		spin_lock_init(&wil->sta[i].tid_rx_lock);
527

528 529 530
	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++)
		spin_lock_init(&wil->vring_tx_data[i].lock);

531 532
	mutex_init(&wil->mutex);
	mutex_init(&wil->wmi_mutex);
533
	mutex_init(&wil->probe_client_mutex);
534
	mutex_init(&wil->p2p_wdev_mutex);
535
	mutex_init(&wil->halp.lock);
536 537

	init_completion(&wil->wmi_ready);
538
	init_completion(&wil->wmi_call);
539
	init_completion(&wil->halp.comp);
540

541
	wil->bcast_vring = -1;
542
	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
543
	setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
Dedy Lansky's avatar
Dedy Lansky committed
544 545
	setup_timer(&wil->p2p.discovery_timer, wil_p2p_discovery_timer_fn,
		    (ulong)wil);
546 547 548

	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
549
	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
550
	INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
551
	INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
552 553

	INIT_LIST_HEAD(&wil->pending_wmi_ev);
554
	INIT_LIST_HEAD(&wil->probe_client_pending);
555
	spin_lock_init(&wil->wmi_ev_lock);
556 557
	spin_lock_init(&wil->net_queue_lock);
	wil->net_queue_stopped = 1;
558
	init_waitqueue_head(&wil->wq);
559

560
	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
561 562 563
	if (!wil->wmi_wq)
		return -EAGAIN;

564 565 566
	wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service");
	if (!wil->wq_service)
		goto out_wmi_wq;
567

568
	wil->last_fw_recovery = jiffies;
569 570 571 572
	wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT;
	wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT;
	wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT;
	wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT;
573

574 575
	if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
		rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
576
	return 0;
577 578 579 580 581

out_wmi_wq:
	destroy_workqueue(wil->wmi_wq);

	return -EAGAIN;
582 583
}

584 585 586 587 588 589
void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
{
	if (wil->platform_ops.bus_request)
		wil->platform_ops.bus_request(wil->platform_handle, kbps);
}

590 591 592 593
/**
 * wil6210_disconnect - disconnect one connection
 * @wil: driver context
 * @bssid: peer to disconnect, NULL to disconnect all
594
 * @reason_code: Reason code for the Disassociation frame
595 596 597 598 599 600
 * @from_event: whether is invoked from FW event handler
 *
 * Disconnect and release associated resources. If invoked not from the
 * FW event handler, issue WMI command(s) to trigger MAC disconnect.
 */
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
601
			u16 reason_code, bool from_event)
602
{
603
	wil_dbg_misc(wil, "disconnect\n");
604

605
	del_timer_sync(&wil->connect_timer);
606
	_wil6210_disconnect(wil, bssid, reason_code, from_event);
607 608 609 610
}

void wil_priv_deinit(struct wil6210_priv *wil)
{
611
	wil_dbg_misc(wil, "priv_deinit\n");
612

613
	wil_set_recovery_state(wil, fw_recovery_idle);
614
	del_timer_sync(&wil->scan_timer);
Dedy Lansky's avatar
Dedy Lansky committed
615
	del_timer_sync(&wil->p2p.discovery_timer);
616
	cancel_work_sync(&wil->disconnect_worker);
617
	cancel_work_sync(&wil->fw_error_worker);
Dedy Lansky's avatar
Dedy Lansky committed
618
	cancel_work_sync(&wil->p2p.discovery_expired_work);
619
	cancel_work_sync(&wil->p2p.delayed_listen_work);
620
	mutex_lock(&wil->mutex);
621
	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
622
	mutex_unlock(&wil->mutex);
623
	wmi_event_flush(wil);
624 625
	wil_probe_client_flush(wil);
	cancel_work_sync(&wil->probe_client_worker);
626
	destroy_workqueue(wil->wq_service);
627 628 629
	destroy_workqueue(wil->wmi_wq);
}

630 631
static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
632 633
	wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
	wil_w(wil, RGF_USER_MAC_CPU_0,  BIT_USER_MAC_CPU_MAN_RST);
634 635 636 637 638
}

static inline void wil_release_cpu(struct wil6210_priv *wil)
{
	/* Start CPU */
639
	wil_w(wil, RGF_USER_USER_CPU_0, 1);
640 641
}

642 643
static void wil_set_oob_mode(struct wil6210_priv *wil, bool enable)
{
644
	wil_info(wil, "enable=%d\n", enable);
645
	if (enable)
646
		wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
647
	else
648 649 650
		wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
}

651
static int wil_target_reset(struct wil6210_priv *wil)
652
{
653
	int delay = 0;
654
	u32 x, x1 = 0;
655

656
	wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
657 658

	/* Clear MAC link up */
659 660 661
	wil_s(wil, RGF_HP_CTRL, BIT(15));
	wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
	wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
662 663

	wil_halt_cpu(wil);
664

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
665
	/* clear all boot loader "ready" bits */
666 667
	wil_w(wil, RGF_USER_BL +
	      offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
668
	/* Clear Fw Download notification */
669
	wil_c(wil, RGF_USER_USAGE_6, BIT(0));
670

671
	wil_s(wil, RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
672 673
	/* XTAL stabilization should take about 3ms */
	usleep_range(5000, 7000);
674
	x = wil_r(wil, RGF_CAF_PLL_LOCK_STATUS);
675 676 677 678
	if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
		wil_err(wil, "Xtal stabilization timeout\n"
			"RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
		return -ETIME;
679
	}
680
	/* switch 10k to XTAL*/
681
	wil_c(wil, RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
682
	/* 40 MHz */
683
	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
684

685 686
	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
687

688 689 690 691
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x000000f0);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
692

693 694
	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
695

696 697 698 699
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
700

701 702 703
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
	/* reset A2 PCIE AHB */
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
704

705
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
706

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
707
	/* wait until device ready. typical time is 20..80 msec */
708
	do {
709
		msleep(RST_DELAY);
710 711 712
		x = wil_r(wil, RGF_USER_BL +
			  offsetof(struct bl_dedicated_registers_v0,
				   boot_loader_ready));
713 714 715 716
		if (x1 != x) {
			wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
			x1 = x;
		}
717
		if (delay++ > RST_COUNT) {
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
718
			wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
719
				x);
720
			return -ETIME;
721
		}
722
	} while (x != BL_READY);
723

724
	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
725

726
	/* enable fix for HW bug related to the SA/DA swap in AP Rx */
727 728
	wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
	      BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
729

730
	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
731
	return 0;
732
}
733

734 735 736 737 738 739 740 741 742 743 744 745 746
static void wil_collect_fw_info(struct wil6210_priv *wil)
{
	struct wiphy *wiphy = wil_to_wiphy(wil);
	u8 retry_short;
	int rc;

	rc = wmi_get_mgmt_retry(wil, &retry_short);
	if (!rc) {
		wiphy->retry_short = retry_short;
		wil_dbg_misc(wil, "FW retry_short: %d\n", retry_short);
	}
}

747 748 749 750 751 752 753 754 755
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
	le32_to_cpus(&r->base);
	le16_to_cpus(&r->entry_size);
	le16_to_cpus(&r->size);
	le32_to_cpus(&r->tail);
	le32_to_cpus(&r->head);
}

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
756 757 758
static int wil_get_bl_info(struct wil6210_priv *wil)
{
	struct net_device *ndev = wil_to_ndev(wil);
759
	struct wiphy *wiphy = wil_to_wiphy(wil);
760 761 762 763 764 765 766 767
	union {
		struct bl_dedicated_registers_v0 bl0;
		struct bl_dedicated_registers_v1 bl1;
	} bl;
	u32 bl_ver;
	u8 *mac;
	u16 rf_status;

768 769 770 771 772 773
	wil_memcpy_fromio_32(&bl, wil->csr + HOSTADDR(RGF_USER_BL),
			     sizeof(bl));
	bl_ver = le32_to_cpu(bl.bl0.boot_loader_struct_version);
	mac = bl.bl0.mac_address;

	if (bl_ver == 0) {
774 775 776 777 778 779 780 781
		le32_to_cpus(&bl.bl0.rf_type);
		le32_to_cpus(&bl.bl0.baseband_type);
		rf_status = 0; /* actually, unknown */
		wil_info(wil,
			 "Boot Loader struct v%d: MAC = %pM RF = 0x%08x bband = 0x%08x\n",
			 bl_ver, mac,
			 bl.bl0.rf_type, bl.bl0.baseband_type);
		wil_info(wil, "Boot Loader build unknown for struct v0\n");
782
	} else {
783 784 785 786 787 788 789 790 791 792 793 794 795 796
		le16_to_cpus(&bl.bl1.rf_type);
		rf_status = le16_to_cpu(bl.bl1.rf_status);
		le32_to_cpus(&bl.bl1.baseband_type);
		le16_to_cpus(&bl.bl1.bl_version_subminor);
		le16_to_cpus(&bl.bl1.bl_version_build);
		wil_info(wil,
			 "Boot Loader struct v%d: MAC = %pM RF = 0x%04x (status 0x%04x) bband = 0x%08x\n",
			 bl_ver, mac,
			 bl.bl1.rf_type, rf_status,
			 bl.bl1.baseband_type);
		wil_info(wil, "Boot Loader build %d.%d.%d.%d\n",
			 bl.bl1.bl_version_major, bl.bl1.bl_version_minor,
			 bl.bl1.bl_version_subminor, bl.bl1.bl_version_build);
	}
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
797

798 799
	if (!is_valid_ether_addr(mac)) {
		wil_err(wil, "BL: Invalid MAC %pM\n", mac);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
800 801 802
		return -EINVAL;
	}

803
	ether_addr_copy(ndev->perm_addr, mac);
804
	ether_addr_copy(wiphy->perm_addr, mac);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
805
	if (!is_valid_ether_addr(ndev->dev_addr))
806 807 808 809 810 811 812
		ether_addr_copy(ndev->dev_addr, mac);

	if (rf_status) {/* bad RF cable? */
		wil_err(wil, "RF communication error 0x%04x",
			rf_status);
		return -EAGAIN;
	}
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
813 814 815 816

	return 0;
}

817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err)
{
	u32 bl_assert_code, bl_assert_blink, bl_magic_number;
	u32 bl_ver = wil_r(wil, RGF_USER_BL +
			   offsetof(struct bl_dedicated_registers_v0,
				    boot_loader_struct_version));

	if (bl_ver < 2)
		return;

	bl_assert_code = wil_r(wil, RGF_USER_BL +
			       offsetof(struct bl_dedicated_registers_v1,
					bl_assert_code));
	bl_assert_blink = wil_r(wil, RGF_USER_BL +
				offsetof(struct bl_dedicated_registers_v1,
					 bl_assert_blink));
	bl_magic_number = wil_r(wil, RGF_USER_BL +
				offsetof(struct bl_dedicated_registers_v1,
					 bl_magic_number));

	if (is_err) {
		wil_err(wil,
			"BL assert code 0x%08x blink 0x%08x magic 0x%08x\n",
			bl_assert_code, bl_assert_blink, bl_magic_number);
	} else {
		wil_dbg_misc(wil,
			     "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n",
			     bl_assert_code, bl_assert_blink, bl_magic_number);
	}
}

848 849 850 851
static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
	ulong to = msecs_to_jiffies(1000);
	ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
852

853 854 855 856
	if (0 == left) {
		wil_err(wil, "Firmware not ready\n");
		return -ETIME;
	} else {
857 858
		wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
			 jiffies_to_msecs(to-left), wil->hw_version);
859 860 861 862
	}
	return 0;
}

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
void wil_abort_scan(struct wil6210_priv *wil, bool sync)
{
	int rc;
	struct cfg80211_scan_info info = {
		.aborted = true,
	};

	lockdep_assert_held(&wil->p2p_wdev_mutex);

	if (!wil->scan_request)
		return;

	wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request);
	del_timer_sync(&wil->scan_timer);
	mutex_unlock(&wil->p2p_wdev_mutex);
	rc = wmi_abort_scan(wil);
	if (!rc && sync)
		wait_event_interruptible_timeout(wil->wq, !wil->scan_request,
						 msecs_to_jiffies(
						 WAIT_FOR_SCAN_ABORT_MS));

	mutex_lock(&wil->p2p_wdev_mutex);
	if (wil->scan_request) {
		cfg80211_scan_done(wil->scan_request, &info);
		wil->scan_request = NULL;
	}
}

891 892 893 894 895
/*
 * We reset all the structures, and we reset the UMAC.
 * After calling this routine, you're expected to reload
 * the firmware.
 */
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
896
int wil_reset(struct wil6210_priv *wil, bool load_fw)
897 898 899
{
	int rc;

900
	wil_dbg_misc(wil, "reset\n");
901

902
	WARN_ON(!mutex_is_locked(&wil->mutex));
903
	WARN_ON(test_bit(wil_status_napi_en, wil->status));
904

905 906 907 908 909 910 911 912 913 914 915
	if (debug_fw) {
		static const u8 mac[ETH_ALEN] = {
			0x00, 0xde, 0xad, 0x12, 0x34, 0x56,
		};
		struct net_device *ndev = wil_to_ndev(wil);

		ether_addr_copy(ndev->perm_addr, mac);
		ether_addr_copy(ndev->dev_addr, ndev->perm_addr);
		return 0;
	}

916 917 918
	if (wil->hw_version == HW_VER_UNKNOWN)
		return -ENODEV;

919 920 921 922
	if (wil->platform_ops.notify) {
		rc = wil->platform_ops.notify(wil->platform_handle,
					      WIL_PLATFORM_EVT_PRE_RESET);
		if (rc)
923 924
			wil_err(wil, "PRE_RESET platform notify failed, rc %d\n",
				rc);
925 926
	}

927 928
	set_bit(wil_status_resetting, wil->status);

929
	cancel_work_sync(&wil->disconnect_worker);
930
	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
931
	wil_bcast_fini(wil);
932

933 934 935
	/* Disable device led before reset*/
	wmi_led_cfg(wil, false);

936 937
	/* prevent NAPI from being scheduled and prevent wmi commands */
	mutex_lock(&wil->wmi_mutex);
938
	bitmap_zero(wil->status, wil_status_last);
939
	mutex_unlock(&wil->wmi_mutex);
940

941
	mutex_lock(&wil->p2p_wdev_mutex);
942
	wil_abort_scan(wil, false);
943
	mutex_unlock(&wil->p2p_wdev_mutex);
944

945
	wil_mask_irq(wil);
946

947 948
	wmi_event_flush(wil);

949
	flush_workqueue(wil->wq_service);
950
	flush_workqueue(wil->wmi_wq);
951

952
	wil_bl_crash_info(wil, false);
953
	wil_disable_irq(wil);
954
	rc = wil_target_reset(wil);
955 956
	wil6210_clear_irq(wil);
	wil_enable_irq(wil);
957
	wil_rx_fini(wil);
958 959
	if (rc) {
		wil_bl_crash_info(wil, true);
960
		return rc;
961
	}
962

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
963
	rc = wil_get_bl_info(wil);
964 965
	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
		rc = 0;
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
966 967 968
	if (rc)
		return rc;

969
	wil_set_oob_mode(wil, oob_mode);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
970
	if (load_fw) {
971 972
		wil_info(wil, "Use firmware <%s> + board <%s>\n",
			 wil->wil_fw_name, WIL_BOARD_FILE_NAME);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
973

974
		wil_halt_cpu(wil);
975
		memset(wil->fw_version, 0, sizeof(wil->fw_version));
976
		/* Loading f/w from the file */
977
		rc = wil_request_firmware(wil, wil->wil_fw_name, true);
978 979
		if (rc)
			return rc;
980
		rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
981 982 983 984
		if (rc)
			return rc;

		/* Mark FW as loaded from host */
985
		wil_s(wil, RGF_USER_USAGE_6, 1);
986

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
987 988 989
		/* clear any interrupts which on-card-firmware
		 * may have set
		 */
990
		wil6210_clear_irq(wil);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
991 992
		/* CAF_ICR - clear and mask */
		/* it is W1C, clear by writing back same value */
993 994
		wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
		wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
995

996 997
		wil_release_cpu(wil);
	}
998

999
	/* init after reset */
1000
	wil->ap_isolate = 0;
1001
	reinit_completion(&wil->wmi_ready);
1002
	reinit_completion(&wil->wmi_call);
1003
	reinit_completion(&wil->halp.comp);
1004

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1005 1006 1007
	if (load_fw) {
		wil_configure_interrupt_moderation(wil);
		wil_unmask_irq(wil);
1008

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1009 1010
		/* we just started MAC, wait for FW ready */
		rc = wil_wait_for_fw_ready(wil);
1011 1012 1013 1014 1015 1016
		if (rc)
			return rc;

		/* check FW is responsive */
		rc = wmi_echo(wil);
		if (rc) {
1017
			wil_err(wil, "wmi_echo failed, rc %d\n", rc);
1018 1019 1020
			return rc;
		}

1021 1022
		wil_collect_fw_info(wil);

1023 1024 1025 1026
		if (wil->platform_ops.notify) {
			rc = wil->platform_ops.notify(wil->platform_handle,
						      WIL_PLATFORM_EVT_FW_RDY);
			if (rc) {
1027 1028
				wil_err(wil, "FW_RDY notify failed, rc %d\n",
					rc);
1029 1030 1031
				rc = 0;
			}
		}
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1032
	}
1033 1034 1035 1036

	return rc;
}

1037 1038 1039
void wil_fw_error_recovery(struct wil6210_priv *wil)
{
	wil_dbg_misc(wil, "starting fw error recovery\n");
1040 1041 1042 1043 1044 1045

	if (test_bit(wil_status_resetting, wil->status)) {
		wil_info(wil, "Reset already in progress\n");
		return;
	}

1046
	wil->recovery_state = fw_recovery_pending;
1047 1048
	schedule_work(&wil->fw_error_worker);
}
1049

1050
int __wil_up(struct wil6210_priv *wil)
1051 1052 1053 1054 1055
{
	struct net_device *ndev = wil_to_ndev(wil);
	struct wireless_dev *wdev = wil->wdev;
	int rc;

1056 1057
	WARN_ON(!mutex_is_locked(&wil->mutex));

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1058
	rc = wil_reset(wil, true);
1059 1060 1061
	if (rc)
		return rc;

1062
	/* Rx VRING. After MAC and beacon */
1063
	rc = wil_rx_init(wil, 1 << rx_ring_order);
1064 1065 1066
	if (rc)
		return rc;

1067 1068
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
1069
		wil_dbg_misc(wil, "type: STATION\n");
1070 1071 1072
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_AP:
1073
		wil_dbg_misc(wil, "type: AP\n");
1074 1075 1076
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_P2P_CLIENT:
1077
		wil_dbg_misc(wil, "type: P2P_CLIENT\n");
1078 1079 1080
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_P2P_GO:
1081
		wil_dbg_misc(wil, "type: P2P_GO\n");
1082 1083 1084
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_MONITOR:
1085
		wil_dbg_misc(wil, "type: Monitor\n");
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
		ndev->type = ARPHRD_IEEE80211_RADIOTAP;
		/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
		break;
	default:
		return -EOPNOTSUPP;
	}

	/* MAC address - pre-requisite for other commands */
	wmi_set_mac_address(wil, ndev->dev_addr);

1096
	wil_dbg_misc(wil, "NAPI enable\n");
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1097 1098
	napi_enable(&wil->napi_rx);
	napi_enable(&wil->napi_tx);
1099
	set_bit(wil_status_napi_en, wil->status);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1100

1101
	wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
1102

1103 1104 1105 1106 1107 1108 1109
	return 0;
}

int wil_up(struct wil6210_priv *wil)
{
	int rc;

1110
	wil_dbg_misc(wil, "up\n");
1111

1112 1113 1114 1115 1116 1117 1118
	mutex_lock(&wil->mutex);
	rc = __wil_up(wil);
	mutex_unlock(&wil->mutex);

	return rc;
}

1119
int __wil_down(struct wil6210_priv *wil)
1120
{
1121 1122
	WARN_ON(!mutex_is_locked(&wil->mutex));

1123 1124
	set_bit(wil_status_resetting, wil->status);

1125
	wil6210_bus_request(wil, 0);
1126

1127
	wil_disable_irq(wil);
1128
	if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
1129 1130 1131 1132 1133
		napi_disable(&wil->napi_rx);
		napi_disable(&wil->napi_tx);
		wil_dbg_misc(wil, "NAPI disable\n");
	}
	wil_enable_irq(wil);
Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1134

1135
	mutex_lock(&wil->p2p_wdev_mutex);
1136 1137
	wil_p2p_stop_radio_operations(wil);
	wil_abort_scan(wil, false);
1138
	mutex_unlock(&wil->p2p_wdev_mutex);
1139

Vladimir Kondratiev's avatar
Vladimir Kondratiev committed
1140
	wil_reset(wil, false);
1141 1142 1143 1144 1145 1146 1147 1148

	return 0;
}

int wil_down(struct wil6210_priv *wil)
{
	int rc;

1149
	wil_dbg_misc(wil, "down\n");
1150

1151
	wil_set_recovery_state(wil, fw_recovery_idle);
1152 1153 1154 1155 1156 1157
	mutex_lock(&wil->mutex);
	rc = __wil_down(wil);
	mutex_unlock(&wil->mutex);

	return rc;
}
1158 1159 1160 1161 1162 1163 1164 1165

int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
{
	int i;
	int rc = -ENOENT;

	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		if ((wil->sta[i].status != wil_sta_unused) &&
1166
		    ether_addr_equal(wil->sta[i].addr, mac)) {
1167 1168 1169 1170 1171 1172 1173
			rc = i;
			break;
		}
	}

	return rc;
}
1174 1175 1176 1177 1178 1179 1180 1181

void wil_halp_vote(struct wil6210_priv *wil)
{
	unsigned long rc;
	unsigned long to_jiffies = msecs_to_jiffies(WAIT_FOR_HALP_VOTE_MS);

	mutex_lock(&wil->halp.lock);

1182
	wil_dbg_irq(wil, "halp_vote: start, HALP ref_cnt (%d)\n",
1183
		    wil->halp.ref_cnt);
1184 1185

	if (++wil->halp.ref_cnt == 1) {
1186
		reinit_completion(&wil->halp.comp);
1187 1188
		wil6210_set_halp(wil);
		rc = wait_for_completion_timeout(&wil->halp.comp, to_jiffies);
1189
		if (!rc) {
1190
			wil_err(wil, "HALP vote timed out\n");
1191 1192 1193
			/* Mask HALP as done in case the interrupt is raised */
			wil6210_mask_halp(wil);
		} else {
1194
			wil_dbg_irq(wil,
1195
				    "halp_vote: HALP vote completed after %d ms\n",
1196
				    jiffies_to_msecs(to_jiffies - rc));
1197
		}
1198 1199
	}

1200
	wil_dbg_irq(wil, "halp_vote: end, HALP ref_cnt (%d)\n",
1201
		    wil->halp.ref_cnt);
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211

	mutex_unlock(&wil->halp.lock);
}

void wil_halp_unvote(struct wil6210_priv *wil)
{
	WARN_ON(wil->halp.ref_cnt == 0);

	mutex_lock(&wil->halp.lock);

1212
	wil_dbg_irq(wil, "halp_unvote: start, HALP ref_cnt (%d)\n",
1213
		    wil->halp.ref_cnt);
1214 1215 1216

	if (--wil->halp.ref_cnt == 0) {
		wil6210_clear_halp(wil);
1217
		wil_dbg_irq(wil, "HALP unvote\n");
1218 1219
	}

1220
	wil_dbg_irq(wil, "halp_unvote:end, HALP ref_cnt (%d)\n",
1221
		    wil->halp.ref_cnt);
1222 1223 1224

	mutex_unlock(&wil->halp.lock);
}