main.c 26.5 KB
Newer Older
1
/*
2
 * Copyright (c) 2012-2015 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"
V
Vladimir Kondratiev 已提交
22
#include "txrx.h"
23
#include "wmi.h"
24
#include "boot_loader.h"
25 26 27

#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
28

V
Vladimir Kondratiev 已提交
29 30 31 32
bool debug_fw; /* = false; */
module_param(debug_fw, bool, S_IRUGO);
MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");

33
bool no_fw_recovery;
V
Vladimir Kondratiev 已提交
34
module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
35
MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
V
Vladimir Kondratiev 已提交
36

37 38 39 40 41 42 43
/* 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;
module_param(rx_ring_overflow_thrsh, ushort, S_IRUGO);
MODULE_PARM_DESC(rx_ring_overflow_thrsh,
		 " RX ring overflow threshold in descriptors.");
V
Vladimir Kondratiev 已提交
44

45 46 47
/* 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.
 */
48
unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD;
49 50 51 52 53 54 55 56 57 58 59
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;

60
	if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU)
61 62 63 64 65
		ret = -EINVAL;

	return ret;
}

66
static const struct kernel_param_ops mtu_max_ops = {
67 68 69 70 71 72 73
	.set = mtu_max_set,
	.get = param_get_uint,
};

module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO);
MODULE_PARM_DESC(mtu_max, " Max MTU value.");

74 75
static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
76
static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

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;
}

95
static const struct kernel_param_ops ring_order_ops = {
96 97 98 99 100 101 102 103
	.set = ring_order_set,
	.get = param_get_uint,
};

module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO);
MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO);
MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
104 105
module_param_cb(bcast_ring_order, &ring_order_ops, &bcast_ring_order, S_IRUGO);
MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order");
106

107 108 109
#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 */

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/*
 * 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++);
}

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++);
}

143
static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
144
			       u16 reason_code, bool from_event)
145
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
146 147
{
	uint i;
148 149
	struct net_device *ndev = wil_to_ndev(wil);
	struct wireless_dev *wdev = wil->wdev;
150
	struct wil_sta_info *sta = &wil->sta[cid];
151

152
	might_sleep();
153 154
	wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
		     sta->status);
155 156

	if (sta->status != wil_sta_unused) {
157
		if (!from_event)
158
			wmi_disconnect_sta(wil, sta->addr, reason_code);
159

160 161 162 163 164 165 166 167 168
		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;
		}
169 170 171
		sta->status = wil_sta_unused;
	}

172
	for (i = 0; i < WIL_STA_TID_NUM; i++) {
173 174
		struct wil_tid_ampdu_rx *r;

175
		spin_lock_bh(&sta->tid_rx_lock);
176 177

		r = sta->tid_rx[i];
178 179
		sta->tid_rx[i] = NULL;
		wil_tid_ampdu_rx_free(wil, r);
180

181
		spin_unlock_bh(&sta->tid_rx_lock);
182 183 184 185 186 187 188 189
	}
	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
		if (wil->vring2cid_tid[i][0] == cid)
			wil_vring_fini_tx(wil, i);
	}
	memset(&sta->stats, 0, sizeof(sta->stats));
}

190
static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
191
				u16 reason_code, bool from_event)
192
{
193
	int cid = -ENOENT;
194
	struct net_device *ndev = wil_to_ndev(wil);
195 196 197
	struct wireless_dev *wdev = wil->wdev;

	might_sleep();
198 199 200 201 202 203 204 205 206 207 208 209 210
	wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid,
		     reason_code, from_event ? "+" : "-");

	/* Cases are:
	 * - disconnect single STA, still connected
	 * - disconnect single STA, already disconnected
	 * - disconnect all
	 *
	 * For "disconnect all", there are 2 options:
	 * - bssid == NULL
	 * - bssid is our MAC address
	 */
	if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) {
211
		cid = wil_find_cid(wil, bssid);
212 213 214 215 216 217
		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");
218
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
219
			wil_disconnect_cid(wil, cid, reason_code, from_event);
220
	}
221 222 223 224 225

	/* link state */
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
226
		wil_bcast_fini(wil);
227 228 229
		netif_tx_stop_all_queues(ndev);
		netif_carrier_off(ndev);

230 231
		if (test_bit(wil_status_fwconnected, wil->status)) {
			clear_bit(wil_status_fwconnected, wil->status);
232
			cfg80211_disconnected(ndev, reason_code,
233
					      NULL, 0, false, GFP_KERNEL);
234
		} else if (test_bit(wil_status_fwconnecting, wil->status)) {
235 236 237 238
			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
						WLAN_STATUS_UNSPECIFIED_FAILURE,
						GFP_KERNEL);
		}
239
		clear_bit(wil_status_fwconnecting, wil->status);
240 241 242
		break;
	default:
		break;
243 244 245 246 247 248 249 250
	}
}

static void wil_disconnect_worker(struct work_struct *work)
{
	struct wil6210_priv *wil = container_of(work,
			struct wil6210_priv, disconnect_worker);

251
	mutex_lock(&wil->mutex);
252
	_wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false);
253
	mutex_unlock(&wil->mutex);
254 255 256 257 258 259
}

static void wil_connect_timer_fn(ulong x)
{
	struct wil6210_priv *wil = (void *)x;

260
	wil_dbg_misc(wil, "Connect timeout\n");
261 262 263 264 265 266 267

	/* reschedule to thread context - disconnect won't
	 * run from atomic context
	 */
	schedule_work(&wil->disconnect_worker);
}

268 269 270 271
static void wil_scan_timer_fn(ulong x)
{
	struct wil6210_priv *wil = (void *)x;

272
	clear_bit(wil_status_fwready, wil->status);
273
	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
274
	wil_fw_error_recovery(wil);
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
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)
{
	wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__,
		     wil->recovery_state, state);

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

V
Vladimir Kondratiev 已提交
301 302
static void wil_fw_error_worker(struct work_struct *work)
{
303 304
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						fw_error_worker);
V
Vladimir Kondratiev 已提交
305 306 307 308
	struct wireless_dev *wdev = wil->wdev;

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

309 310 311 312 313
	if (!netif_running(wil_to_ndev(wil))) {
		wil_info(wil, "No recovery - interface is down\n");
		return;
	}

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
	/* 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;

331
	mutex_lock(&wil->mutex);
V
Vladimir Kondratiev 已提交
332 333 334 335
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
	case NL80211_IFTYPE_MONITOR:
336
		wil_info(wil, "fw error recovery requested (try %d)...\n",
337
			 wil->recovery_count);
338 339 340 341 342
		if (!no_fw_recovery)
			wil->recovery_state = fw_recovery_running;
		if (0 != wil_wait_for_recovery(wil))
			break;

343 344
		__wil_down(wil);
		__wil_up(wil);
V
Vladimir Kondratiev 已提交
345 346 347
		break;
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
348
		wil_info(wil, "No recovery for AP-like interface\n");
V
Vladimir Kondratiev 已提交
349 350 351
		/* recovery in these modes is done by upper layers */
		break;
	default:
352 353
		wil_err(wil, "No recovery - unknown interface type %d\n",
			wdev->iftype);
V
Vladimir Kondratiev 已提交
354 355
		break;
	}
356
	mutex_unlock(&wil->mutex);
V
Vladimir Kondratiev 已提交
357 358
}

359 360 361
static int wil_find_free_vring(struct wil6210_priv *wil)
{
	int i;
362

363 364 365 366 367 368 369
	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
		if (!wil->vring_tx[i].va)
			return i;
	}
	return -EINVAL;
}

370 371 372 373 374 375 376 377 378 379 380
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;

381
	wil->bcast_vring = ri;
382
	rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
383 384
	if (rc)
		wil->bcast_vring = -1;
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399

	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);
}

400 401 402 403 404
static void wil_connect_worker(struct work_struct *work)
{
	int rc;
	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
						connect_worker);
405 406
	struct net_device *ndev = wil_to_ndev(wil);

407
	int cid = wil->pending_connect_cid;
408
	int ringid = wil_find_free_vring(wil);
409 410 411 412 413 414 415 416

	if (cid < 0) {
		wil_err(wil, "No connection pending\n");
		return;
	}

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

417
	rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
418
	wil->pending_connect_cid = -1;
419 420
	if (rc == 0) {
		wil->sta[cid].status = wil_sta_connected;
421
		netif_tx_wake_all_queues(ndev);
422 423 424
	} else {
		wil->sta[cid].status = wil_sta_unused;
	}
425 426
}

427 428
int wil_priv_init(struct wil6210_priv *wil)
{
429 430
	uint i;

431
	wil_dbg_misc(wil, "%s()\n", __func__);
432

433
	memset(wil->sta, 0, sizeof(wil->sta));
434 435
	for (i = 0; i < WIL6210_MAX_CID; i++)
		spin_lock_init(&wil->sta[i].tid_rx_lock);
436

437 438
	mutex_init(&wil->mutex);
	mutex_init(&wil->wmi_mutex);
V
Vladimir Kondratiev 已提交
439
	mutex_init(&wil->back_rx_mutex);
440
	mutex_init(&wil->back_tx_mutex);
441
	mutex_init(&wil->probe_client_mutex);
442 443

	init_completion(&wil->wmi_ready);
444
	init_completion(&wil->wmi_call);
445 446

	wil->pending_connect_cid = -1;
447
	wil->bcast_vring = -1;
448
	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
449
	setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
450

451
	INIT_WORK(&wil->connect_worker, wil_connect_worker);
452 453
	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
V
Vladimir Kondratiev 已提交
454
	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
V
Vladimir Kondratiev 已提交
455
	INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker);
456
	INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker);
457
	INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
458 459

	INIT_LIST_HEAD(&wil->pending_wmi_ev);
V
Vladimir Kondratiev 已提交
460
	INIT_LIST_HEAD(&wil->back_rx_pending);
461
	INIT_LIST_HEAD(&wil->back_tx_pending);
462
	INIT_LIST_HEAD(&wil->probe_client_pending);
463
	spin_lock_init(&wil->wmi_ev_lock);
464
	init_waitqueue_head(&wil->wq);
465

V
Vladimir Kondratiev 已提交
466
	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
467 468 469
	if (!wil->wmi_wq)
		return -EAGAIN;

V
Vladimir Kondratiev 已提交
470 471 472
	wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service");
	if (!wil->wq_service)
		goto out_wmi_wq;
473

474
	wil->last_fw_recovery = jiffies;
475 476 477 478
	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;
479

480 481
	if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
		rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
482
	return 0;
V
Vladimir Kondratiev 已提交
483 484 485 486 487

out_wmi_wq:
	destroy_workqueue(wil->wmi_wq);

	return -EAGAIN;
488 489
}

490 491 492 493
/**
 * wil6210_disconnect - disconnect one connection
 * @wil: driver context
 * @bssid: peer to disconnect, NULL to disconnect all
494
 * @reason_code: Reason code for the Disassociation frame
495 496 497 498 499 500
 * @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,
501
			u16 reason_code, bool from_event)
502
{
503 504
	wil_dbg_misc(wil, "%s()\n", __func__);

505
	del_timer_sync(&wil->connect_timer);
506
	_wil6210_disconnect(wil, bssid, reason_code, from_event);
507 508 509 510
}

void wil_priv_deinit(struct wil6210_priv *wil)
{
511 512
	wil_dbg_misc(wil, "%s()\n", __func__);

513
	wil_set_recovery_state(wil, fw_recovery_idle);
514
	del_timer_sync(&wil->scan_timer);
515
	cancel_work_sync(&wil->disconnect_worker);
V
Vladimir Kondratiev 已提交
516
	cancel_work_sync(&wil->fw_error_worker);
517
	mutex_lock(&wil->mutex);
518
	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
519
	mutex_unlock(&wil->mutex);
520
	wmi_event_flush(wil);
V
Vladimir Kondratiev 已提交
521 522
	wil_back_rx_flush(wil);
	cancel_work_sync(&wil->back_rx_worker);
523 524
	wil_back_tx_flush(wil);
	cancel_work_sync(&wil->back_tx_worker);
525 526
	wil_probe_client_flush(wil);
	cancel_work_sync(&wil->probe_client_worker);
V
Vladimir Kondratiev 已提交
527
	destroy_workqueue(wil->wq_service);
528 529 530
	destroy_workqueue(wil->wmi_wq);
}

V
Vladimir Kondratiev 已提交
531 532
static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
533 534
	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);
V
Vladimir Kondratiev 已提交
535 536 537 538 539
}

static inline void wil_release_cpu(struct wil6210_priv *wil)
{
	/* Start CPU */
540
	wil_w(wil, RGF_USER_USER_CPU_0, 1);
V
Vladimir Kondratiev 已提交
541 542
}

543
static int wil_target_reset(struct wil6210_priv *wil)
544
{
V
Vladimir Kondratiev 已提交
545
	int delay = 0;
546
	u32 x, x1 = 0;
547

548
	wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
549 550

	/* Clear MAC link up */
551 552 553
	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);
V
Vladimir Kondratiev 已提交
554 555

	wil_halt_cpu(wil);
556

V
Vladimir Kondratiev 已提交
557
	/* clear all boot loader "ready" bits */
558 559
	wil_w(wil, RGF_USER_BL +
	      offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
V
Vladimir Kondratiev 已提交
560
	/* Clear Fw Download notification */
561
	wil_c(wil, RGF_USER_USAGE_6, BIT(0));
V
Vladimir Kondratiev 已提交
562

563
	wil_s(wil, RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
564 565
	/* XTAL stabilization should take about 3ms */
	usleep_range(5000, 7000);
566
	x = wil_r(wil, RGF_CAF_PLL_LOCK_STATUS);
567 568 569 570
	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;
571
	}
572
	/* switch 10k to XTAL*/
573
	wil_c(wil, RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
574
	/* 40 MHz */
575
	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
576

577 578
	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);
579

580 581 582 583
	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);
584

585 586
	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);
587

588 589 590 591
	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);
592

593 594 595
	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);
596

597
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
598

V
Vladimir Kondratiev 已提交
599
	/* wait until device ready. typical time is 20..80 msec */
600
	do {
601
		msleep(RST_DELAY);
602 603 604
		x = wil_r(wil, RGF_USER_BL +
			  offsetof(struct bl_dedicated_registers_v0,
				   boot_loader_ready));
605 606 607 608
		if (x1 != x) {
			wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
			x1 = x;
		}
609
		if (delay++ > RST_COUNT) {
V
Vladimir Kondratiev 已提交
610
			wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
V
Vladimir Kondratiev 已提交
611
				x);
612
			return -ETIME;
613
		}
614
	} while (x != BL_READY);
615

616
	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
617

618
	/* enable fix for HW bug related to the SA/DA swap in AP Rx */
619 620
	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);
621

622
	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
623
	return 0;
V
Vladimir Kondratiev 已提交
624
}
625 626 627 628 629 630 631 632 633 634

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);
}

V
Vladimir Kondratiev 已提交
635 636 637
static int wil_get_bl_info(struct wil6210_priv *wil)
{
	struct net_device *ndev = wil_to_ndev(wil);
638 639 640 641 642 643 644 645
	union {
		struct bl_dedicated_registers_v0 bl0;
		struct bl_dedicated_registers_v1 bl1;
	} bl;
	u32 bl_ver;
	u8 *mac;
	u16 rf_status;

646 647 648 649 650 651
	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) {
652 653 654 655 656 657 658 659
		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");
660
	} else {
661 662 663 664 665 666 667 668 669 670 671 672 673 674
		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);
	}
V
Vladimir Kondratiev 已提交
675

676 677
	if (!is_valid_ether_addr(mac)) {
		wil_err(wil, "BL: Invalid MAC %pM\n", mac);
V
Vladimir Kondratiev 已提交
678 679 680
		return -EINVAL;
	}

681
	ether_addr_copy(ndev->perm_addr, mac);
V
Vladimir Kondratiev 已提交
682
	if (!is_valid_ether_addr(ndev->dev_addr))
683 684 685 686 687 688 689
		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;
	}
V
Vladimir Kondratiev 已提交
690 691 692 693

	return 0;
}

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
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);
	}
}

725 726 727 728
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);
729

730 731 732 733
	if (0 == left) {
		wil_err(wil, "Firmware not ready\n");
		return -ETIME;
	} else {
734 735
		wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
			 jiffies_to_msecs(to-left), wil->hw_version);
736 737 738 739 740 741 742 743 744
	}
	return 0;
}

/*
 * We reset all the structures, and we reset the UMAC.
 * After calling this routine, you're expected to reload
 * the firmware.
 */
V
Vladimir Kondratiev 已提交
745
int wil_reset(struct wil6210_priv *wil, bool load_fw)
746 747 748
{
	int rc;

749 750
	wil_dbg_misc(wil, "%s()\n", __func__);

751
	WARN_ON(!mutex_is_locked(&wil->mutex));
752
	WARN_ON(test_bit(wil_status_napi_en, wil->status));
753

V
Vladimir Kondratiev 已提交
754 755 756 757 758 759 760 761 762 763 764
	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;
	}

765 766 767
	if (wil->hw_version == HW_VER_UNKNOWN)
		return -ENODEV;

768
	cancel_work_sync(&wil->disconnect_worker);
769
	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
770
	wil_bcast_fini(wil);
771

772 773
	/* prevent NAPI from being scheduled */
	bitmap_zero(wil->status, wil_status_last);
774

V
Vladimir Kondratiev 已提交
775 776 777
	if (wil->scan_request) {
		wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
			     wil->scan_request);
778
		del_timer_sync(&wil->scan_timer);
V
Vladimir Kondratiev 已提交
779 780 781 782
		cfg80211_scan_done(wil->scan_request, true);
		wil->scan_request = NULL;
	}

783
	wil_mask_irq(wil);
784

785 786
	wmi_event_flush(wil);

V
Vladimir Kondratiev 已提交
787
	flush_workqueue(wil->wq_service);
788
	flush_workqueue(wil->wmi_wq);
789

790
	wil_bl_crash_info(wil, false);
791
	rc = wil_target_reset(wil);
792
	wil_rx_fini(wil);
793 794
	if (rc) {
		wil_bl_crash_info(wil, true);
795
		return rc;
796
	}
797

V
Vladimir Kondratiev 已提交
798
	rc = wil_get_bl_info(wil);
799 800
	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
		rc = 0;
V
Vladimir Kondratiev 已提交
801 802 803 804 805 806 807
	if (rc)
		return rc;

	if (load_fw) {
		wil_info(wil, "Use firmware <%s> + board <%s>\n", WIL_FW_NAME,
			 WIL_FW2_NAME);

V
Vladimir Kondratiev 已提交
808 809 810 811 812
		wil_halt_cpu(wil);
		/* Loading f/w from the file */
		rc = wil_request_firmware(wil, WIL_FW_NAME);
		if (rc)
			return rc;
V
Vladimir Kondratiev 已提交
813 814 815 816 817
		rc = wil_request_firmware(wil, WIL_FW2_NAME);
		if (rc)
			return rc;

		/* Mark FW as loaded from host */
818
		wil_s(wil, RGF_USER_USAGE_6, 1);
V
Vladimir Kondratiev 已提交
819

V
Vladimir Kondratiev 已提交
820 821 822
		/* clear any interrupts which on-card-firmware
		 * may have set
		 */
V
Vladimir Kondratiev 已提交
823
		wil6210_clear_irq(wil);
V
Vladimir Kondratiev 已提交
824 825
		/* CAF_ICR - clear and mask */
		/* it is W1C, clear by writing back same value */
826 827
		wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
		wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
V
Vladimir Kondratiev 已提交
828

V
Vladimir Kondratiev 已提交
829 830
		wil_release_cpu(wil);
	}
831

832 833
	/* init after reset */
	wil->pending_connect_cid = -1;
834
	wil->ap_isolate = 0;
835
	reinit_completion(&wil->wmi_ready);
836
	reinit_completion(&wil->wmi_call);
837

V
Vladimir Kondratiev 已提交
838 839 840
	if (load_fw) {
		wil_configure_interrupt_moderation(wil);
		wil_unmask_irq(wil);
841

V
Vladimir Kondratiev 已提交
842 843
		/* we just started MAC, wait for FW ready */
		rc = wil_wait_for_fw_ready(wil);
844 845
		if (rc == 0) /* check FW is responsive */
			rc = wmi_echo(wil);
V
Vladimir Kondratiev 已提交
846
	}
847 848 849 850

	return rc;
}

V
Vladimir Kondratiev 已提交
851 852 853
void wil_fw_error_recovery(struct wil6210_priv *wil)
{
	wil_dbg_misc(wil, "starting fw error recovery\n");
854
	wil->recovery_state = fw_recovery_pending;
V
Vladimir Kondratiev 已提交
855 856
	schedule_work(&wil->fw_error_worker);
}
857

858
int __wil_up(struct wil6210_priv *wil)
859 860 861 862 863
{
	struct net_device *ndev = wil_to_ndev(wil);
	struct wireless_dev *wdev = wil->wdev;
	int rc;

864 865
	WARN_ON(!mutex_is_locked(&wil->mutex));

V
Vladimir Kondratiev 已提交
866
	rc = wil_reset(wil, true);
867 868 869
	if (rc)
		return rc;

870
	/* Rx VRING. After MAC and beacon */
871
	rc = wil_rx_init(wil, 1 << rx_ring_order);
872 873 874
	if (rc)
		return rc;

875 876
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
877
		wil_dbg_misc(wil, "type: STATION\n");
878 879 880
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_AP:
881
		wil_dbg_misc(wil, "type: AP\n");
882 883 884
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_P2P_CLIENT:
885
		wil_dbg_misc(wil, "type: P2P_CLIENT\n");
886 887 888
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_P2P_GO:
889
		wil_dbg_misc(wil, "type: P2P_GO\n");
890 891 892
		ndev->type = ARPHRD_ETHER;
		break;
	case NL80211_IFTYPE_MONITOR:
893
		wil_dbg_misc(wil, "type: Monitor\n");
894 895 896 897 898 899 900 901 902 903
		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);

904
	wil_dbg_misc(wil, "NAPI enable\n");
V
Vladimir Kondratiev 已提交
905 906
	napi_enable(&wil->napi_rx);
	napi_enable(&wil->napi_tx);
907
	set_bit(wil_status_napi_en, wil->status);
V
Vladimir Kondratiev 已提交
908

909 910 911 912
	if (wil->platform_ops.bus_request)
		wil->platform_ops.bus_request(wil->platform_handle,
					      WIL_MAX_BUS_REQUEST_KBPS);

913 914 915 916 917 918 919
	return 0;
}

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

920 921
	wil_dbg_misc(wil, "%s()\n", __func__);

922 923 924 925 926 927 928
	mutex_lock(&wil->mutex);
	rc = __wil_up(wil);
	mutex_unlock(&wil->mutex);

	return rc;
}

929
int __wil_down(struct wil6210_priv *wil)
930
{
931 932 933
	int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
			WAIT_FOR_DISCONNECT_INTERVAL_MS;

934 935
	WARN_ON(!mutex_is_locked(&wil->mutex));

936 937 938
	if (wil->platform_ops.bus_request)
		wil->platform_ops.bus_request(wil->platform_handle, 0);

939
	wil_disable_irq(wil);
940
	if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
941 942 943 944 945
		napi_disable(&wil->napi_rx);
		napi_disable(&wil->napi_tx);
		wil_dbg_misc(wil, "NAPI disable\n");
	}
	wil_enable_irq(wil);
V
Vladimir Kondratiev 已提交
946

947
	if (wil->scan_request) {
948 949
		wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
			     wil->scan_request);
950
		del_timer_sync(&wil->scan_timer);
951 952 953 954
		cfg80211_scan_done(wil->scan_request, true);
		wil->scan_request = NULL;
	}

955 956
	if (test_bit(wil_status_fwconnected, wil->status) ||
	    test_bit(wil_status_fwconnecting, wil->status))
957 958 959 960 961
		wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);

	/* make sure wil is idle (not connected) */
	mutex_unlock(&wil->mutex);
	while (iter--) {
962 963
		int idle = !test_bit(wil_status_fwconnected, wil->status) &&
			   !test_bit(wil_status_fwconnecting, wil->status);
964 965 966 967 968 969 970 971 972
		if (idle)
			break;
		msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
	}
	mutex_lock(&wil->mutex);

	if (!iter)
		wil_err(wil, "timeout waiting for idle FW/HW\n");

V
Vladimir Kondratiev 已提交
973
	wil_reset(wil, false);
974 975 976 977 978 979 980 981

	return 0;
}

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

982 983
	wil_dbg_misc(wil, "%s()\n", __func__);

984
	wil_set_recovery_state(wil, fw_recovery_idle);
985 986 987 988 989 990
	mutex_lock(&wil->mutex);
	rc = __wil_down(wil);
	mutex_unlock(&wil->mutex);

	return rc;
}
991 992 993 994 995 996 997 998

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) &&
999
		    ether_addr_equal(wil->sta[i].addr, mac)) {
1000 1001 1002 1003 1004 1005 1006
			rc = i;
			break;
		}
	}

	return rc;
}