12be7d22fSVladimir Kondratiev /*
2849a564bSDedy Lansky  * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
32be7d22fSVladimir Kondratiev  *
42be7d22fSVladimir Kondratiev  * Permission to use, copy, modify, and/or distribute this software for any
52be7d22fSVladimir Kondratiev  * purpose with or without fee is hereby granted, provided that the above
62be7d22fSVladimir Kondratiev  * copyright notice and this permission notice appear in all copies.
72be7d22fSVladimir Kondratiev  *
82be7d22fSVladimir Kondratiev  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
92be7d22fSVladimir Kondratiev  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
102be7d22fSVladimir Kondratiev  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
112be7d22fSVladimir Kondratiev  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122be7d22fSVladimir Kondratiev  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132be7d22fSVladimir Kondratiev  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
142be7d22fSVladimir Kondratiev  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
152be7d22fSVladimir Kondratiev  */
162be7d22fSVladimir Kondratiev 
172be7d22fSVladimir Kondratiev #include <linux/moduleparam.h>
182be7d22fSVladimir Kondratiev #include <linux/if_arp.h>
19108d1eb6SVladimir Kondratiev #include <linux/etherdevice.h>
202be7d22fSVladimir Kondratiev 
212be7d22fSVladimir Kondratiev #include "wil6210.h"
22b4490f42SVladimir Kondratiev #include "txrx.h"
23f172b563SDedy Lansky #include "wmi.h"
24f1ad8c93SVladimir Kondratiev #include "boot_loader.h"
25f172b563SDedy Lansky 
26349214c1SMaya Erez #define WAIT_FOR_HALP_VOTE_MS 100
27035859a5SMaya Erez #define WAIT_FOR_SCAN_ABORT_MS 1000
28349214c1SMaya Erez 
29bfc2dc7aSVladimir Kondratiev bool debug_fw; /* = false; */
3078484c44SMaya Erez module_param(debug_fw, bool, 0444);
31bfc2dc7aSVladimir Kondratiev MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");
32bfc2dc7aSVladimir Kondratiev 
33b8c31b5dSLior David static u8 oob_mode;
34b8c31b5dSLior David module_param(oob_mode, byte, 0444);
351f1a361aSLior David MODULE_PARM_DESC(oob_mode,
361f1a361aSLior David 		 " enable out of the box (OOB) mode in FW, for diagnostics and certification");
371f1a361aSLior David 
38c33407a8SVladimir Kondratiev bool no_fw_recovery;
3978484c44SMaya Erez module_param(no_fw_recovery, bool, 0644);
40c33407a8SVladimir Kondratiev MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
41ed6f9dc6SVladimir Kondratiev 
42ab954628SVladimir Kondratiev /* if not set via modparam, will be set to default value of 1/8 of
43ab954628SVladimir Kondratiev  * rx ring size during init flow
44ab954628SVladimir Kondratiev  */
45ab954628SVladimir Kondratiev unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT;
4678484c44SMaya Erez module_param(rx_ring_overflow_thrsh, ushort, 0444);
47ab954628SVladimir Kondratiev MODULE_PARM_DESC(rx_ring_overflow_thrsh,
48ab954628SVladimir Kondratiev 		 " RX ring overflow threshold in descriptors.");
49b6b1b0ecSVladimir Kondratiev 
509a06bec9SVladimir Kondratiev /* We allow allocation of more than 1 page buffers to support large packets.
519a06bec9SVladimir Kondratiev  * It is suboptimal behavior performance wise in case MTU above page size.
529a06bec9SVladimir Kondratiev  */
53c44690a1SVladimir Kondratiev unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD;
549a06bec9SVladimir Kondratiev static int mtu_max_set(const char *val, const struct kernel_param *kp)
559a06bec9SVladimir Kondratiev {
569a06bec9SVladimir Kondratiev 	int ret;
579a06bec9SVladimir Kondratiev 
589a06bec9SVladimir Kondratiev 	/* sets mtu_max directly. no need to restore it in case of
599a06bec9SVladimir Kondratiev 	 * illegal value since we assume this will fail insmod
609a06bec9SVladimir Kondratiev 	 */
619a06bec9SVladimir Kondratiev 	ret = param_set_uint(val, kp);
629a06bec9SVladimir Kondratiev 	if (ret)
639a06bec9SVladimir Kondratiev 		return ret;
649a06bec9SVladimir Kondratiev 
654590d812SVladimir Kondratiev 	if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU)
669a06bec9SVladimir Kondratiev 		ret = -EINVAL;
679a06bec9SVladimir Kondratiev 
689a06bec9SVladimir Kondratiev 	return ret;
699a06bec9SVladimir Kondratiev }
709a06bec9SVladimir Kondratiev 
719c27847dSLuis R. Rodriguez static const struct kernel_param_ops mtu_max_ops = {
729a06bec9SVladimir Kondratiev 	.set = mtu_max_set,
739a06bec9SVladimir Kondratiev 	.get = param_get_uint,
749a06bec9SVladimir Kondratiev };
759a06bec9SVladimir Kondratiev 
7678484c44SMaya Erez module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, 0444);
779a06bec9SVladimir Kondratiev MODULE_PARM_DESC(mtu_max, " Max MTU value.");
789a06bec9SVladimir Kondratiev 
79d3762b40SVladimir Kondratiev static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
80d3762b40SVladimir Kondratiev static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
8141d6b093SVladimir Kondratiev static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
82d3762b40SVladimir Kondratiev 
83d3762b40SVladimir Kondratiev static int ring_order_set(const char *val, const struct kernel_param *kp)
84d3762b40SVladimir Kondratiev {
85d3762b40SVladimir Kondratiev 	int ret;
86d3762b40SVladimir Kondratiev 	uint x;
87d3762b40SVladimir Kondratiev 
88d3762b40SVladimir Kondratiev 	ret = kstrtouint(val, 0, &x);
89d3762b40SVladimir Kondratiev 	if (ret)
90d3762b40SVladimir Kondratiev 		return ret;
91d3762b40SVladimir Kondratiev 
92d3762b40SVladimir Kondratiev 	if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
93d3762b40SVladimir Kondratiev 		return -EINVAL;
94d3762b40SVladimir Kondratiev 
95d3762b40SVladimir Kondratiev 	*((uint *)kp->arg) = x;
96d3762b40SVladimir Kondratiev 
97d3762b40SVladimir Kondratiev 	return 0;
98d3762b40SVladimir Kondratiev }
99d3762b40SVladimir Kondratiev 
1009c27847dSLuis R. Rodriguez static const struct kernel_param_ops ring_order_ops = {
101d3762b40SVladimir Kondratiev 	.set = ring_order_set,
102d3762b40SVladimir Kondratiev 	.get = param_get_uint,
103d3762b40SVladimir Kondratiev };
104d3762b40SVladimir Kondratiev 
10578484c44SMaya Erez module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, 0444);
106d3762b40SVladimir Kondratiev MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
10778484c44SMaya Erez module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, 0444);
108d3762b40SVladimir Kondratiev MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
10978484c44SMaya Erez module_param_cb(bcast_ring_order, &ring_order_ops, &bcast_ring_order, 0444);
110d507d1b7SVladimir Kondratiev MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order");
111d3762b40SVladimir Kondratiev 
112520d68e7SVladimir Kondratiev #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
113520d68e7SVladimir Kondratiev #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
114520d68e7SVladimir Kondratiev 
1152be7d22fSVladimir Kondratiev /*
1162be7d22fSVladimir Kondratiev  * Due to a hardware issue,
1172be7d22fSVladimir Kondratiev  * one has to read/write to/from NIC in 32-bit chunks;
1182be7d22fSVladimir Kondratiev  * regular memcpy_fromio and siblings will
1192be7d22fSVladimir Kondratiev  * not work on 64-bit platform - it uses 64-bit transactions
1202be7d22fSVladimir Kondratiev  *
1212be7d22fSVladimir Kondratiev  * Force 32-bit transactions to enable NIC on 64-bit platforms
1222be7d22fSVladimir Kondratiev  *
1232be7d22fSVladimir Kondratiev  * To avoid byte swap on big endian host, __raw_{read|write}l
1242be7d22fSVladimir Kondratiev  * should be used - {read|write}l would swap bytes to provide
1252be7d22fSVladimir Kondratiev  * little endian on PCI value in host endianness.
1262be7d22fSVladimir Kondratiev  */
1272be7d22fSVladimir Kondratiev void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
1282be7d22fSVladimir Kondratiev 			  size_t count)
1292be7d22fSVladimir Kondratiev {
1302be7d22fSVladimir Kondratiev 	u32 *d = dst;
1312be7d22fSVladimir Kondratiev 	const volatile u32 __iomem *s = src;
1322be7d22fSVladimir Kondratiev 
1332be7d22fSVladimir Kondratiev 	/* size_t is unsigned, if (count%4 != 0) it will wrap */
1342be7d22fSVladimir Kondratiev 	for (count += 4; count > 4; count -= 4)
1352be7d22fSVladimir Kondratiev 		*d++ = __raw_readl(s++);
1362be7d22fSVladimir Kondratiev }
1372be7d22fSVladimir Kondratiev 
138349214c1SMaya Erez void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst,
139349214c1SMaya Erez 				 const volatile void __iomem *src, size_t count)
140349214c1SMaya Erez {
141349214c1SMaya Erez 	wil_halp_vote(wil);
142349214c1SMaya Erez 	wil_memcpy_fromio_32(dst, src, count);
143349214c1SMaya Erez 	wil_halp_unvote(wil);
144349214c1SMaya Erez }
145349214c1SMaya Erez 
1462be7d22fSVladimir Kondratiev void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
1472be7d22fSVladimir Kondratiev 			size_t count)
1482be7d22fSVladimir Kondratiev {
1492be7d22fSVladimir Kondratiev 	volatile u32 __iomem *d = dst;
1502be7d22fSVladimir Kondratiev 	const u32 *s = src;
1512be7d22fSVladimir Kondratiev 
1522be7d22fSVladimir Kondratiev 	for (count += 4; count > 4; count -= 4)
1532be7d22fSVladimir Kondratiev 		__raw_writel(*s++, d++);
1542be7d22fSVladimir Kondratiev }
1552be7d22fSVladimir Kondratiev 
156349214c1SMaya Erez void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil,
157349214c1SMaya Erez 			       volatile void __iomem *dst,
158349214c1SMaya Erez 			       const void *src, size_t count)
159349214c1SMaya Erez {
160349214c1SMaya Erez 	wil_halp_vote(wil);
161349214c1SMaya Erez 	wil_memcpy_toio_32(dst, src, count);
162349214c1SMaya Erez 	wil_halp_unvote(wil);
163349214c1SMaya Erez }
164349214c1SMaya Erez 
165b516fcc5SVladimir Kondratiev static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
1664821e6d8SVladimir Kondratiev 			       u16 reason_code, bool from_event)
167bd33273bSVladimir Kondratiev __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
1682be7d22fSVladimir Kondratiev {
16991886b0bSVladimir Kondratiev 	uint i;
170fc58f681SVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
171fc58f681SVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
172b4490f42SVladimir Kondratiev 	struct wil_sta_info *sta = &wil->sta[cid];
1738fe59627SVladimir Kondratiev 
174bd33273bSVladimir Kondratiev 	might_sleep();
175af3db60aSLazar Alexei 	wil_dbg_misc(wil, "disconnect_cid: CID %d, status %d\n",
176af3db60aSLazar Alexei 		     cid, sta->status);
17758527421SVladimir Kondratiev 	/* inform upper/lower layers */
1784d55a0a1SVladimir Kondratiev 	if (sta->status != wil_sta_unused) {
179849a564bSDedy Lansky 		if (!from_event) {
180849a564bSDedy Lansky 			bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
181849a564bSDedy Lansky 						disable_ap_sme : false;
182849a564bSDedy Lansky 			wmi_disconnect_sta(wil, sta->addr, reason_code,
183849a564bSDedy Lansky 					   true, del_sta);
184849a564bSDedy Lansky 		}
185b516fcc5SVladimir Kondratiev 
186fc58f681SVladimir Kondratiev 		switch (wdev->iftype) {
187fc58f681SVladimir Kondratiev 		case NL80211_IFTYPE_AP:
188fc58f681SVladimir Kondratiev 		case NL80211_IFTYPE_P2P_GO:
189fc58f681SVladimir Kondratiev 			/* AP-like interface */
190fc58f681SVladimir Kondratiev 			cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
191fc58f681SVladimir Kondratiev 			break;
192fc58f681SVladimir Kondratiev 		default:
193fc58f681SVladimir Kondratiev 			break;
194fc58f681SVladimir Kondratiev 		}
1954d55a0a1SVladimir Kondratiev 		sta->status = wil_sta_unused;
1964d55a0a1SVladimir Kondratiev 	}
19758527421SVladimir Kondratiev 	/* reorder buffers */
198b4490f42SVladimir Kondratiev 	for (i = 0; i < WIL_STA_TID_NUM; i++) {
199ec81b5adSDedy Lansky 		struct wil_tid_ampdu_rx *r;
200ec81b5adSDedy Lansky 
201bd33273bSVladimir Kondratiev 		spin_lock_bh(&sta->tid_rx_lock);
202ec81b5adSDedy Lansky 
203ec81b5adSDedy Lansky 		r = sta->tid_rx[i];
204b4490f42SVladimir Kondratiev 		sta->tid_rx[i] = NULL;
205b4490f42SVladimir Kondratiev 		wil_tid_ampdu_rx_free(wil, r);
206ec81b5adSDedy Lansky 
207bd33273bSVladimir Kondratiev 		spin_unlock_bh(&sta->tid_rx_lock);
208b4490f42SVladimir Kondratiev 	}
20958527421SVladimir Kondratiev 	/* crypto context */
21058527421SVladimir Kondratiev 	memset(sta->tid_crypto_rx, 0, sizeof(sta->tid_crypto_rx));
21158527421SVladimir Kondratiev 	memset(&sta->group_crypto_rx, 0, sizeof(sta->group_crypto_rx));
21258527421SVladimir Kondratiev 	/* release vrings */
21391886b0bSVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
21491886b0bSVladimir Kondratiev 		if (wil->vring2cid_tid[i][0] == cid)
21591886b0bSVladimir Kondratiev 			wil_vring_fini_tx(wil, i);
21691886b0bSVladimir Kondratiev 	}
21758527421SVladimir Kondratiev 	/* statistics */
21891886b0bSVladimir Kondratiev 	memset(&sta->stats, 0, sizeof(sta->stats));
219b4490f42SVladimir Kondratiev }
220b4490f42SVladimir Kondratiev 
221f9e3033fSDedy Lansky static bool wil_is_connected(struct wil6210_priv *wil)
22254eaa8c6SMaya Erez {
22354eaa8c6SMaya Erez 	int i;
22454eaa8c6SMaya Erez 
22554eaa8c6SMaya Erez 	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
22654eaa8c6SMaya Erez 		if (wil->sta[i].status == wil_sta_connected)
22754eaa8c6SMaya Erez 			return true;
22854eaa8c6SMaya Erez 	}
22954eaa8c6SMaya Erez 
23054eaa8c6SMaya Erez 	return false;
23154eaa8c6SMaya Erez }
23254eaa8c6SMaya Erez 
233b516fcc5SVladimir Kondratiev static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
2344821e6d8SVladimir Kondratiev 				u16 reason_code, bool from_event)
23591886b0bSVladimir Kondratiev {
23691886b0bSVladimir Kondratiev 	int cid = -ENOENT;
23791886b0bSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
23891886b0bSVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
23991886b0bSVladimir Kondratiev 
2402690c4c0SLior David 	if (unlikely(!ndev))
2412690c4c0SLior David 		return;
2422690c4c0SLior David 
24391886b0bSVladimir Kondratiev 	might_sleep();
244af3db60aSLazar Alexei 	wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
245100106d7SVladimir Kondratiev 		 reason_code, from_event ? "+" : "-");
24691886b0bSVladimir Kondratiev 
247100106d7SVladimir Kondratiev 	/* Cases are:
248100106d7SVladimir Kondratiev 	 * - disconnect single STA, still connected
249100106d7SVladimir Kondratiev 	 * - disconnect single STA, already disconnected
250100106d7SVladimir Kondratiev 	 * - disconnect all
251100106d7SVladimir Kondratiev 	 *
25268682b41SVladimir Kondratiev 	 * For "disconnect all", there are 3 options:
253100106d7SVladimir Kondratiev 	 * - bssid == NULL
25468682b41SVladimir Kondratiev 	 * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
255100106d7SVladimir Kondratiev 	 * - bssid is our MAC address
256100106d7SVladimir Kondratiev 	 */
25768682b41SVladimir Kondratiev 	if (bssid && !is_broadcast_ether_addr(bssid) &&
25868682b41SVladimir Kondratiev 	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
259100106d7SVladimir Kondratiev 		cid = wil_find_cid(wil, bssid);
260100106d7SVladimir Kondratiev 		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
261100106d7SVladimir Kondratiev 			     bssid, cid, reason_code);
26291886b0bSVladimir Kondratiev 		if (cid >= 0) /* disconnect 1 peer */
2634821e6d8SVladimir Kondratiev 			wil_disconnect_cid(wil, cid, reason_code, from_event);
264100106d7SVladimir Kondratiev 	} else { /* all */
265100106d7SVladimir Kondratiev 		wil_dbg_misc(wil, "Disconnect all\n");
26691886b0bSVladimir Kondratiev 		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
2674821e6d8SVladimir Kondratiev 			wil_disconnect_cid(wil, cid, reason_code, from_event);
268100106d7SVladimir Kondratiev 	}
26991886b0bSVladimir Kondratiev 
27091886b0bSVladimir Kondratiev 	/* link state */
27191886b0bSVladimir Kondratiev 	switch (wdev->iftype) {
27291886b0bSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
27391886b0bSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
27441d6b093SVladimir Kondratiev 		wil_bcast_fini(wil);
275f9e3033fSDedy Lansky 		wil_update_net_queues_bh(wil, NULL, true);
276c5e96c91SDedy Lansky 		netif_carrier_off(ndev);
2779953a782SLior David 		wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
278c5e96c91SDedy Lansky 
2799419b6a2SVladimir Kondratiev 		if (test_bit(wil_status_fwconnected, wil->status)) {
2809419b6a2SVladimir Kondratiev 			clear_bit(wil_status_fwconnected, wil->status);
2814821e6d8SVladimir Kondratiev 			cfg80211_disconnected(ndev, reason_code,
2823b56c15fSDedy Lansky 					      NULL, 0,
2833b56c15fSDedy Lansky 					      wil->locally_generated_disc,
2843b56c15fSDedy Lansky 					      GFP_KERNEL);
2853b56c15fSDedy Lansky 			wil->locally_generated_disc = false;
2869419b6a2SVladimir Kondratiev 		} else if (test_bit(wil_status_fwconnecting, wil->status)) {
2872be7d22fSVladimir Kondratiev 			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
2882be7d22fSVladimir Kondratiev 						WLAN_STATUS_UNSPECIFIED_FAILURE,
2892be7d22fSVladimir Kondratiev 						GFP_KERNEL);
290bcdd49b0SDedy Lansky 			wil->bss = NULL;
2912be7d22fSVladimir Kondratiev 		}
2929419b6a2SVladimir Kondratiev 		clear_bit(wil_status_fwconnecting, wil->status);
29391886b0bSVladimir Kondratiev 		break;
29454eaa8c6SMaya Erez 	case NL80211_IFTYPE_AP:
29554eaa8c6SMaya Erez 	case NL80211_IFTYPE_P2P_GO:
296f9e3033fSDedy Lansky 		if (!wil_is_connected(wil)) {
297f9e3033fSDedy Lansky 			wil_update_net_queues_bh(wil, NULL, true);
29854eaa8c6SMaya Erez 			clear_bit(wil_status_fwconnected, wil->status);
299f9e3033fSDedy Lansky 		} else {
300f9e3033fSDedy Lansky 			wil_update_net_queues_bh(wil, NULL, false);
301f9e3033fSDedy Lansky 		}
30254eaa8c6SMaya Erez 		break;
30391886b0bSVladimir Kondratiev 	default:
30491886b0bSVladimir Kondratiev 		break;
30591886b0bSVladimir Kondratiev 	}
3062be7d22fSVladimir Kondratiev }
3072be7d22fSVladimir Kondratiev 
3082be7d22fSVladimir Kondratiev static void wil_disconnect_worker(struct work_struct *work)
3092be7d22fSVladimir Kondratiev {
3102be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = container_of(work,
3112be7d22fSVladimir Kondratiev 			struct wil6210_priv, disconnect_worker);
312628639b1SDedy Lansky 	struct net_device *ndev = wil_to_ndev(wil);
313628639b1SDedy Lansky 	int rc;
314628639b1SDedy Lansky 	struct {
315628639b1SDedy Lansky 		struct wmi_cmd_hdr wmi;
316628639b1SDedy Lansky 		struct wmi_disconnect_event evt;
317628639b1SDedy Lansky 	} __packed reply;
3182be7d22fSVladimir Kondratiev 
319628639b1SDedy Lansky 	if (test_bit(wil_status_fwconnected, wil->status))
320628639b1SDedy Lansky 		/* connect succeeded after all */
321628639b1SDedy Lansky 		return;
322628639b1SDedy Lansky 
323628639b1SDedy Lansky 	if (!test_bit(wil_status_fwconnecting, wil->status))
324628639b1SDedy Lansky 		/* already disconnected */
325628639b1SDedy Lansky 		return;
326628639b1SDedy Lansky 
327628639b1SDedy Lansky 	rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
328628639b1SDedy Lansky 		      WMI_DISCONNECT_EVENTID, &reply, sizeof(reply),
329628639b1SDedy Lansky 		      WIL6210_DISCONNECT_TO_MS);
330628639b1SDedy Lansky 	if (rc) {
331628639b1SDedy Lansky 		wil_err(wil, "disconnect error %d\n", rc);
332628639b1SDedy Lansky 		return;
333628639b1SDedy Lansky 	}
334628639b1SDedy Lansky 
335628639b1SDedy Lansky 	wil_update_net_queues_bh(wil, NULL, true);
336628639b1SDedy Lansky 	netif_carrier_off(ndev);
337628639b1SDedy Lansky 	cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0,
338628639b1SDedy Lansky 				WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
339628639b1SDedy Lansky 	clear_bit(wil_status_fwconnecting, wil->status);
3402be7d22fSVladimir Kondratiev }
3412be7d22fSVladimir Kondratiev 
3422be7d22fSVladimir Kondratiev static void wil_connect_timer_fn(ulong x)
3432be7d22fSVladimir Kondratiev {
3442be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = (void *)x;
3450916d9f2SMaya Erez 	bool q;
3462be7d22fSVladimir Kondratiev 
3470916d9f2SMaya Erez 	wil_err(wil, "Connect timeout detected, disconnect station\n");
3482be7d22fSVladimir Kondratiev 
3492be7d22fSVladimir Kondratiev 	/* reschedule to thread context - disconnect won't
3500916d9f2SMaya Erez 	 * run from atomic context.
3510916d9f2SMaya Erez 	 * queue on wmi_wq to prevent race with connect event.
3522be7d22fSVladimir Kondratiev 	 */
3530916d9f2SMaya Erez 	q = queue_work(wil->wmi_wq, &wil->disconnect_worker);
3540916d9f2SMaya Erez 	wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
3552be7d22fSVladimir Kondratiev }
3562be7d22fSVladimir Kondratiev 
357047e5d74SVladimir Kondratiev static void wil_scan_timer_fn(ulong x)
358047e5d74SVladimir Kondratiev {
359047e5d74SVladimir Kondratiev 	struct wil6210_priv *wil = (void *)x;
360047e5d74SVladimir Kondratiev 
3619419b6a2SVladimir Kondratiev 	clear_bit(wil_status_fwready, wil->status);
362047e5d74SVladimir Kondratiev 	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
3638ad6600fSVladimir Kondratiev 	wil_fw_error_recovery(wil);
364047e5d74SVladimir Kondratiev }
365047e5d74SVladimir Kondratiev 
366c33407a8SVladimir Kondratiev static int wil_wait_for_recovery(struct wil6210_priv *wil)
367c33407a8SVladimir Kondratiev {
368c33407a8SVladimir Kondratiev 	if (wait_event_interruptible(wil->wq, wil->recovery_state !=
369c33407a8SVladimir Kondratiev 				     fw_recovery_pending)) {
370c33407a8SVladimir Kondratiev 		wil_err(wil, "Interrupt, canceling recovery\n");
371c33407a8SVladimir Kondratiev 		return -ERESTARTSYS;
372c33407a8SVladimir Kondratiev 	}
373c33407a8SVladimir Kondratiev 	if (wil->recovery_state != fw_recovery_running) {
374c33407a8SVladimir Kondratiev 		wil_info(wil, "Recovery cancelled\n");
375c33407a8SVladimir Kondratiev 		return -EINTR;
376c33407a8SVladimir Kondratiev 	}
377c33407a8SVladimir Kondratiev 	wil_info(wil, "Proceed with recovery\n");
378c33407a8SVladimir Kondratiev 	return 0;
379c33407a8SVladimir Kondratiev }
380c33407a8SVladimir Kondratiev 
381c33407a8SVladimir Kondratiev void wil_set_recovery_state(struct wil6210_priv *wil, int state)
382c33407a8SVladimir Kondratiev {
383af3db60aSLazar Alexei 	wil_dbg_misc(wil, "set_recovery_state: %d -> %d\n",
384c33407a8SVladimir Kondratiev 		     wil->recovery_state, state);
385c33407a8SVladimir Kondratiev 
386c33407a8SVladimir Kondratiev 	wil->recovery_state = state;
387c33407a8SVladimir Kondratiev 	wake_up_interruptible(&wil->wq);
388c33407a8SVladimir Kondratiev }
389c33407a8SVladimir Kondratiev 
390375a173fSLior David bool wil_is_recovery_blocked(struct wil6210_priv *wil)
391375a173fSLior David {
392375a173fSLior David 	return no_fw_recovery && (wil->recovery_state == fw_recovery_pending);
393375a173fSLior David }
394375a173fSLior David 
395ed6f9dc6SVladimir Kondratiev static void wil_fw_error_worker(struct work_struct *work)
396ed6f9dc6SVladimir Kondratiev {
397c33407a8SVladimir Kondratiev 	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
398c33407a8SVladimir Kondratiev 						fw_error_worker);
399ed6f9dc6SVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
400ed6f9dc6SVladimir Kondratiev 
401ed6f9dc6SVladimir Kondratiev 	wil_dbg_misc(wil, "fw error worker\n");
402ed6f9dc6SVladimir Kondratiev 
403cded9369SVladimir Kondratiev 	if (!netif_running(wil_to_ndev(wil))) {
404cded9369SVladimir Kondratiev 		wil_info(wil, "No recovery - interface is down\n");
405cded9369SVladimir Kondratiev 		return;
406cded9369SVladimir Kondratiev 	}
407cded9369SVladimir Kondratiev 
408fc219eedSVladimir Kondratiev 	/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
409fc219eedSVladimir Kondratiev 	 * passed since last recovery attempt
410fc219eedSVladimir Kondratiev 	 */
411fc219eedSVladimir Kondratiev 	if (time_is_after_jiffies(wil->last_fw_recovery +
412fc219eedSVladimir Kondratiev 				  WIL6210_FW_RECOVERY_TO))
413fc219eedSVladimir Kondratiev 		wil->recovery_count++;
414fc219eedSVladimir Kondratiev 	else
415fc219eedSVladimir Kondratiev 		wil->recovery_count = 1; /* fw was alive for a long time */
416fc219eedSVladimir Kondratiev 
417fc219eedSVladimir Kondratiev 	if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
418fc219eedSVladimir Kondratiev 		wil_err(wil, "too many recovery attempts (%d), giving up\n",
419fc219eedSVladimir Kondratiev 			wil->recovery_count);
420fc219eedSVladimir Kondratiev 		return;
421fc219eedSVladimir Kondratiev 	}
422fc219eedSVladimir Kondratiev 
423fc219eedSVladimir Kondratiev 	wil->last_fw_recovery = jiffies;
424fc219eedSVladimir Kondratiev 
425dfb5b098SLior David 	wil_info(wil, "fw error recovery requested (try %d)...\n",
426dfb5b098SLior David 		 wil->recovery_count);
427dfb5b098SLior David 	if (!no_fw_recovery)
428dfb5b098SLior David 		wil->recovery_state = fw_recovery_running;
429dfb5b098SLior David 	if (wil_wait_for_recovery(wil) != 0)
430dfb5b098SLior David 		return;
431dfb5b098SLior David 
4329c3bde56SVladimir Kondratiev 	mutex_lock(&wil->mutex);
433ed6f9dc6SVladimir Kondratiev 	switch (wdev->iftype) {
434ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
435ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
436ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
437dfb5b098SLior David 		/* silent recovery, upper layers will see disconnect */
43873d839aeSVladimir Kondratiev 		__wil_down(wil);
43973d839aeSVladimir Kondratiev 		__wil_up(wil);
440ed6f9dc6SVladimir Kondratiev 		break;
441ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_AP:
442ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
443e240537bSVladimir Kondratiev 		wil_info(wil, "No recovery for AP-like interface\n");
444ed6f9dc6SVladimir Kondratiev 		/* recovery in these modes is done by upper layers */
445ed6f9dc6SVladimir Kondratiev 		break;
446ed6f9dc6SVladimir Kondratiev 	default:
447e240537bSVladimir Kondratiev 		wil_err(wil, "No recovery - unknown interface type %d\n",
448e240537bSVladimir Kondratiev 			wdev->iftype);
449ed6f9dc6SVladimir Kondratiev 		break;
450ed6f9dc6SVladimir Kondratiev 	}
4519c3bde56SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
452ed6f9dc6SVladimir Kondratiev }
453ed6f9dc6SVladimir Kondratiev 
4549a177384SVladimir Kondratiev static int wil_find_free_vring(struct wil6210_priv *wil)
4559a177384SVladimir Kondratiev {
4569a177384SVladimir Kondratiev 	int i;
4578fe59627SVladimir Kondratiev 
4589a177384SVladimir Kondratiev 	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
4599a177384SVladimir Kondratiev 		if (!wil->vring_tx[i].va)
4609a177384SVladimir Kondratiev 			return i;
4619a177384SVladimir Kondratiev 	}
4629a177384SVladimir Kondratiev 	return -EINVAL;
4639a177384SVladimir Kondratiev }
4649a177384SVladimir Kondratiev 
4650916d9f2SMaya Erez int wil_tx_init(struct wil6210_priv *wil, int cid)
4660916d9f2SMaya Erez {
4670916d9f2SMaya Erez 	int rc = -EINVAL, ringid;
4680916d9f2SMaya Erez 
4690916d9f2SMaya Erez 	if (cid < 0) {
4700916d9f2SMaya Erez 		wil_err(wil, "No connection pending\n");
4710916d9f2SMaya Erez 		goto out;
4720916d9f2SMaya Erez 	}
4730916d9f2SMaya Erez 	ringid = wil_find_free_vring(wil);
4740916d9f2SMaya Erez 	if (ringid < 0) {
4750916d9f2SMaya Erez 		wil_err(wil, "No free vring found\n");
4760916d9f2SMaya Erez 		goto out;
4770916d9f2SMaya Erez 	}
4780916d9f2SMaya Erez 
4790916d9f2SMaya Erez 	wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
4800916d9f2SMaya Erez 		    cid, ringid);
4810916d9f2SMaya Erez 
4820916d9f2SMaya Erez 	rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
4830916d9f2SMaya Erez 	if (rc)
4840916d9f2SMaya Erez 		wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n",
4850916d9f2SMaya Erez 			cid, ringid);
4860916d9f2SMaya Erez 
4870916d9f2SMaya Erez out:
4880916d9f2SMaya Erez 	return rc;
4890916d9f2SMaya Erez }
4900916d9f2SMaya Erez 
49141d6b093SVladimir Kondratiev int wil_bcast_init(struct wil6210_priv *wil)
49241d6b093SVladimir Kondratiev {
49341d6b093SVladimir Kondratiev 	int ri = wil->bcast_vring, rc;
49441d6b093SVladimir Kondratiev 
49541d6b093SVladimir Kondratiev 	if ((ri >= 0) && wil->vring_tx[ri].va)
49641d6b093SVladimir Kondratiev 		return 0;
49741d6b093SVladimir Kondratiev 
49841d6b093SVladimir Kondratiev 	ri = wil_find_free_vring(wil);
49941d6b093SVladimir Kondratiev 	if (ri < 0)
50041d6b093SVladimir Kondratiev 		return ri;
50141d6b093SVladimir Kondratiev 
50241d6b093SVladimir Kondratiev 	wil->bcast_vring = ri;
503230d8442SVladimir Kondratiev 	rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
504230d8442SVladimir Kondratiev 	if (rc)
505230d8442SVladimir Kondratiev 		wil->bcast_vring = -1;
50641d6b093SVladimir Kondratiev 
50741d6b093SVladimir Kondratiev 	return rc;
50841d6b093SVladimir Kondratiev }
50941d6b093SVladimir Kondratiev 
51041d6b093SVladimir Kondratiev void wil_bcast_fini(struct wil6210_priv *wil)
51141d6b093SVladimir Kondratiev {
51241d6b093SVladimir Kondratiev 	int ri = wil->bcast_vring;
51341d6b093SVladimir Kondratiev 
51441d6b093SVladimir Kondratiev 	if (ri < 0)
51541d6b093SVladimir Kondratiev 		return;
51641d6b093SVladimir Kondratiev 
51741d6b093SVladimir Kondratiev 	wil->bcast_vring = -1;
51841d6b093SVladimir Kondratiev 	wil_vring_fini_tx(wil, ri);
51941d6b093SVladimir Kondratiev }
52041d6b093SVladimir Kondratiev 
5212be7d22fSVladimir Kondratiev int wil_priv_init(struct wil6210_priv *wil)
5222be7d22fSVladimir Kondratiev {
523ec81b5adSDedy Lansky 	uint i;
524ec81b5adSDedy Lansky 
525af3db60aSLazar Alexei 	wil_dbg_misc(wil, "priv_init\n");
5262be7d22fSVladimir Kondratiev 
5273df2cd36SVladimir Kondratiev 	memset(wil->sta, 0, sizeof(wil->sta));
528ec81b5adSDedy Lansky 	for (i = 0; i < WIL6210_MAX_CID; i++)
529ec81b5adSDedy Lansky 		spin_lock_init(&wil->sta[i].tid_rx_lock);
5303df2cd36SVladimir Kondratiev 
531875e9439SMaya Erez 	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++)
532875e9439SMaya Erez 		spin_lock_init(&wil->vring_tx_data[i].lock);
533875e9439SMaya Erez 
5342be7d22fSVladimir Kondratiev 	mutex_init(&wil->mutex);
5352be7d22fSVladimir Kondratiev 	mutex_init(&wil->wmi_mutex);
53640822a90SVladimir Kondratiev 	mutex_init(&wil->probe_client_mutex);
5374332cac1SLior David 	mutex_init(&wil->p2p_wdev_mutex);
538349214c1SMaya Erez 	mutex_init(&wil->halp.lock);
5392be7d22fSVladimir Kondratiev 
5402be7d22fSVladimir Kondratiev 	init_completion(&wil->wmi_ready);
54159502647SDedy Lansky 	init_completion(&wil->wmi_call);
542349214c1SMaya Erez 	init_completion(&wil->halp.comp);
5432be7d22fSVladimir Kondratiev 
54441d6b093SVladimir Kondratiev 	wil->bcast_vring = -1;
5452be7d22fSVladimir Kondratiev 	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
546047e5d74SVladimir Kondratiev 	setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
547e6d68341SDedy Lansky 	setup_timer(&wil->p2p.discovery_timer, wil_p2p_discovery_timer_fn,
548e6d68341SDedy Lansky 		    (ulong)wil);
5492be7d22fSVladimir Kondratiev 
5502be7d22fSVladimir Kondratiev 	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
5512be7d22fSVladimir Kondratiev 	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
552ed6f9dc6SVladimir Kondratiev 	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
55340822a90SVladimir Kondratiev 	INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
554bb6743f7SLior David 	INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
5552be7d22fSVladimir Kondratiev 
5562be7d22fSVladimir Kondratiev 	INIT_LIST_HEAD(&wil->pending_wmi_ev);
55740822a90SVladimir Kondratiev 	INIT_LIST_HEAD(&wil->probe_client_pending);
5582be7d22fSVladimir Kondratiev 	spin_lock_init(&wil->wmi_ev_lock);
559f9e3033fSDedy Lansky 	spin_lock_init(&wil->net_queue_lock);
560f9e3033fSDedy Lansky 	wil->net_queue_stopped = 1;
561c33407a8SVladimir Kondratiev 	init_waitqueue_head(&wil->wq);
5622be7d22fSVladimir Kondratiev 
5632be7d22fSVladimir Kondratiev 	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
5642be7d22fSVladimir Kondratiev 	if (!wil->wmi_wq)
5652be7d22fSVladimir Kondratiev 		return -EAGAIN;
5662be7d22fSVladimir Kondratiev 
5673277213fSVladimir Kondratiev 	wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service");
5683277213fSVladimir Kondratiev 	if (!wil->wq_service)
5693277213fSVladimir Kondratiev 		goto out_wmi_wq;
5702be7d22fSVladimir Kondratiev 
571fc219eedSVladimir Kondratiev 	wil->last_fw_recovery = jiffies;
5721f80af2eSVladimir Shulman 	wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT;
5731f80af2eSVladimir Shulman 	wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT;
5741f80af2eSVladimir Shulman 	wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT;
5751f80af2eSVladimir Shulman 	wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT;
576fc219eedSVladimir Kondratiev 
577ab954628SVladimir Kondratiev 	if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT)
578ab954628SVladimir Kondratiev 		rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT;
5792be7d22fSVladimir Kondratiev 	return 0;
5803277213fSVladimir Kondratiev 
5813277213fSVladimir Kondratiev out_wmi_wq:
5823277213fSVladimir Kondratiev 	destroy_workqueue(wil->wmi_wq);
5833277213fSVladimir Kondratiev 
5843277213fSVladimir Kondratiev 	return -EAGAIN;
5852be7d22fSVladimir Kondratiev }
5862be7d22fSVladimir Kondratiev 
5879953a782SLior David void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
5889953a782SLior David {
5899953a782SLior David 	if (wil->platform_ops.bus_request)
5909953a782SLior David 		wil->platform_ops.bus_request(wil->platform_handle, kbps);
5919953a782SLior David }
5929953a782SLior David 
593b516fcc5SVladimir Kondratiev /**
594b516fcc5SVladimir Kondratiev  * wil6210_disconnect - disconnect one connection
595b516fcc5SVladimir Kondratiev  * @wil: driver context
596b516fcc5SVladimir Kondratiev  * @bssid: peer to disconnect, NULL to disconnect all
5974821e6d8SVladimir Kondratiev  * @reason_code: Reason code for the Disassociation frame
598b516fcc5SVladimir Kondratiev  * @from_event: whether is invoked from FW event handler
599b516fcc5SVladimir Kondratiev  *
600b516fcc5SVladimir Kondratiev  * Disconnect and release associated resources. If invoked not from the
601b516fcc5SVladimir Kondratiev  * FW event handler, issue WMI command(s) to trigger MAC disconnect.
602b516fcc5SVladimir Kondratiev  */
603b516fcc5SVladimir Kondratiev void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
6044821e6d8SVladimir Kondratiev 			u16 reason_code, bool from_event)
6052be7d22fSVladimir Kondratiev {
606af3db60aSLazar Alexei 	wil_dbg_misc(wil, "disconnect\n");
6079cf10d62SVladimir Kondratiev 
6082be7d22fSVladimir Kondratiev 	del_timer_sync(&wil->connect_timer);
6094821e6d8SVladimir Kondratiev 	_wil6210_disconnect(wil, bssid, reason_code, from_event);
6102be7d22fSVladimir Kondratiev }
6112be7d22fSVladimir Kondratiev 
6122be7d22fSVladimir Kondratiev void wil_priv_deinit(struct wil6210_priv *wil)
6132be7d22fSVladimir Kondratiev {
614af3db60aSLazar Alexei 	wil_dbg_misc(wil, "priv_deinit\n");
6159cf10d62SVladimir Kondratiev 
616c33407a8SVladimir Kondratiev 	wil_set_recovery_state(wil, fw_recovery_idle);
617047e5d74SVladimir Kondratiev 	del_timer_sync(&wil->scan_timer);
618e6d68341SDedy Lansky 	del_timer_sync(&wil->p2p.discovery_timer);
6192be7d22fSVladimir Kondratiev 	cancel_work_sync(&wil->disconnect_worker);
620ed6f9dc6SVladimir Kondratiev 	cancel_work_sync(&wil->fw_error_worker);
621e6d68341SDedy Lansky 	cancel_work_sync(&wil->p2p.discovery_expired_work);
622bb6743f7SLior David 	cancel_work_sync(&wil->p2p.delayed_listen_work);
623097638a0SVladimir Kondratiev 	mutex_lock(&wil->mutex);
6244821e6d8SVladimir Kondratiev 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
625097638a0SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
6262be7d22fSVladimir Kondratiev 	wmi_event_flush(wil);
62740822a90SVladimir Kondratiev 	wil_probe_client_flush(wil);
62840822a90SVladimir Kondratiev 	cancel_work_sync(&wil->probe_client_worker);
6293277213fSVladimir Kondratiev 	destroy_workqueue(wil->wq_service);
6302be7d22fSVladimir Kondratiev 	destroy_workqueue(wil->wmi_wq);
6312be7d22fSVladimir Kondratiev }
6322be7d22fSVladimir Kondratiev 
633151a9706SVladimir Kondratiev static inline void wil_halt_cpu(struct wil6210_priv *wil)
634151a9706SVladimir Kondratiev {
635b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
636b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_MAC_CPU_0,  BIT_USER_MAC_CPU_MAN_RST);
637151a9706SVladimir Kondratiev }
638151a9706SVladimir Kondratiev 
639151a9706SVladimir Kondratiev static inline void wil_release_cpu(struct wil6210_priv *wil)
640151a9706SVladimir Kondratiev {
641151a9706SVladimir Kondratiev 	/* Start CPU */
642b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_USER_CPU_0, 1);
643151a9706SVladimir Kondratiev }
644151a9706SVladimir Kondratiev 
645b8c31b5dSLior David static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode)
6461f1a361aSLior David {
647b8c31b5dSLior David 	wil_info(wil, "oob_mode to %d\n", mode);
648b8c31b5dSLior David 	switch (mode) {
649b8c31b5dSLior David 	case 0:
650b8c31b5dSLior David 		wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE |
651b8c31b5dSLior David 		      BIT_USER_OOB_R2_MODE);
652b8c31b5dSLior David 		break;
653b8c31b5dSLior David 	case 1:
654b8c31b5dSLior David 		wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE);
6551f1a361aSLior David 		wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
656b8c31b5dSLior David 		break;
657b8c31b5dSLior David 	case 2:
6581f1a361aSLior David 		wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
659b8c31b5dSLior David 		wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE);
660b8c31b5dSLior David 		break;
661b8c31b5dSLior David 	default:
662b8c31b5dSLior David 		wil_err(wil, "invalid oob_mode: %d\n", mode);
663b8c31b5dSLior David 	}
6641f1a361aSLior David }
6651f1a361aSLior David 
666bbb2adc7SVladimir Kondratiev static int wil_target_reset(struct wil6210_priv *wil)
6672be7d22fSVladimir Kondratiev {
66898a65b59SVladimir Kondratiev 	int delay = 0;
669bb6c8dccSVladimir Kondratiev 	u32 x, x1 = 0;
67036b10a72SVladimir Kondratiev 
6711aeda13bSVladimir Kondratiev 	wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
6726508281bSVladimir Kondratiev 
6736508281bSVladimir Kondratiev 	/* Clear MAC link up */
674b9eeb512SVladimir Kondratiev 	wil_s(wil, RGF_HP_CTRL, BIT(15));
675b9eeb512SVladimir Kondratiev 	wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
676b9eeb512SVladimir Kondratiev 	wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
677151a9706SVladimir Kondratiev 
678151a9706SVladimir Kondratiev 	wil_halt_cpu(wil);
6792be7d22fSVladimir Kondratiev 
6802cd0f021SVladimir Kondratiev 	/* clear all boot loader "ready" bits */
681b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_BL +
682f1ad8c93SVladimir Kondratiev 	      offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
683cce47711SVladimir Kondratiev 	/* Clear Fw Download notification */
684b9eeb512SVladimir Kondratiev 	wil_c(wil, RGF_USER_USAGE_6, BIT(0));
685cce47711SVladimir Kondratiev 
686b9eeb512SVladimir Kondratiev 	wil_s(wil, RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
68748516298SVladimir Kondratiev 	/* XTAL stabilization should take about 3ms */
68848516298SVladimir Kondratiev 	usleep_range(5000, 7000);
689b9eeb512SVladimir Kondratiev 	x = wil_r(wil, RGF_CAF_PLL_LOCK_STATUS);
69048516298SVladimir Kondratiev 	if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
69148516298SVladimir Kondratiev 		wil_err(wil, "Xtal stabilization timeout\n"
69248516298SVladimir Kondratiev 			"RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
69348516298SVladimir Kondratiev 		return -ETIME;
69448516298SVladimir Kondratiev 	}
69548516298SVladimir Kondratiev 	/* switch 10k to XTAL*/
696b9eeb512SVladimir Kondratiev 	wil_c(wil, RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
69748516298SVladimir Kondratiev 	/* 40 MHz */
698b9eeb512SVladimir Kondratiev 	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
69948516298SVladimir Kondratiev 
700b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
701b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
7026508281bSVladimir Kondratiev 
703b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
704b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
705b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x000000f0);
706b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
7072be7d22fSVladimir Kondratiev 
708b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
709b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
7106508281bSVladimir Kondratiev 
711b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
712b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
713b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
714b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
7152be7d22fSVladimir Kondratiev 
716b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
717b9eeb512SVladimir Kondratiev 	/* reset A2 PCIE AHB */
718b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
7196508281bSVladimir Kondratiev 
720b9eeb512SVladimir Kondratiev 	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
7212be7d22fSVladimir Kondratiev 
7222cd0f021SVladimir Kondratiev 	/* wait until device ready. typical time is 20..80 msec */
72336b10a72SVladimir Kondratiev 	do {
724520d68e7SVladimir Kondratiev 		msleep(RST_DELAY);
725b9eeb512SVladimir Kondratiev 		x = wil_r(wil, RGF_USER_BL +
726b9eeb512SVladimir Kondratiev 			  offsetof(struct bl_dedicated_registers_v0,
727f1ad8c93SVladimir Kondratiev 				   boot_loader_ready));
728bb6c8dccSVladimir Kondratiev 		if (x1 != x) {
729bb6c8dccSVladimir Kondratiev 			wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
730bb6c8dccSVladimir Kondratiev 			x1 = x;
731bb6c8dccSVladimir Kondratiev 		}
732520d68e7SVladimir Kondratiev 		if (delay++ > RST_COUNT) {
7332cd0f021SVladimir Kondratiev 			wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
73448516298SVladimir Kondratiev 				x);
735bbb2adc7SVladimir Kondratiev 			return -ETIME;
73636b10a72SVladimir Kondratiev 		}
737f1ad8c93SVladimir Kondratiev 	} while (x != BL_READY);
73836b10a72SVladimir Kondratiev 
739b9eeb512SVladimir Kondratiev 	wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
740972072aaSVladimir Kondratiev 
741e3351277SVladimir Kondratiev 	/* enable fix for HW bug related to the SA/DA swap in AP Rx */
742b9eeb512SVladimir Kondratiev 	wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
743e3351277SVladimir Kondratiev 	      BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
744e3351277SVladimir Kondratiev 
745520d68e7SVladimir Kondratiev 	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
746bbb2adc7SVladimir Kondratiev 	return 0;
747151a9706SVladimir Kondratiev }
7482be7d22fSVladimir Kondratiev 
7493fea18d0SLior David static void wil_collect_fw_info(struct wil6210_priv *wil)
7503fea18d0SLior David {
7513fea18d0SLior David 	struct wiphy *wiphy = wil_to_wiphy(wil);
7523fea18d0SLior David 	u8 retry_short;
7533fea18d0SLior David 	int rc;
7543fea18d0SLior David 
7553fea18d0SLior David 	rc = wmi_get_mgmt_retry(wil, &retry_short);
7563fea18d0SLior David 	if (!rc) {
7573fea18d0SLior David 		wiphy->retry_short = retry_short;
7583fea18d0SLior David 		wil_dbg_misc(wil, "FW retry_short: %d\n", retry_short);
7593fea18d0SLior David 	}
7603fea18d0SLior David }
7613fea18d0SLior David 
7622be7d22fSVladimir Kondratiev void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
7632be7d22fSVladimir Kondratiev {
7642be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->base);
7652be7d22fSVladimir Kondratiev 	le16_to_cpus(&r->entry_size);
7662be7d22fSVladimir Kondratiev 	le16_to_cpus(&r->size);
7672be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->tail);
7682be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->head);
7692be7d22fSVladimir Kondratiev }
7702be7d22fSVladimir Kondratiev 
7712cd0f021SVladimir Kondratiev static int wil_get_bl_info(struct wil6210_priv *wil)
7722cd0f021SVladimir Kondratiev {
7732cd0f021SVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
774b42f1196SHamad Kadmany 	struct wiphy *wiphy = wil_to_wiphy(wil);
775f1ad8c93SVladimir Kondratiev 	union {
776f1ad8c93SVladimir Kondratiev 		struct bl_dedicated_registers_v0 bl0;
777f1ad8c93SVladimir Kondratiev 		struct bl_dedicated_registers_v1 bl1;
778f1ad8c93SVladimir Kondratiev 	} bl;
779f1ad8c93SVladimir Kondratiev 	u32 bl_ver;
780f1ad8c93SVladimir Kondratiev 	u8 *mac;
781f1ad8c93SVladimir Kondratiev 	u16 rf_status;
7822cd0f021SVladimir Kondratiev 
783f1ad8c93SVladimir Kondratiev 	wil_memcpy_fromio_32(&bl, wil->csr + HOSTADDR(RGF_USER_BL),
78419c871ceSVladimir Kondratiev 			     sizeof(bl));
78519c871ceSVladimir Kondratiev 	bl_ver = le32_to_cpu(bl.bl0.boot_loader_struct_version);
78619c871ceSVladimir Kondratiev 	mac = bl.bl0.mac_address;
78719c871ceSVladimir Kondratiev 
78819c871ceSVladimir Kondratiev 	if (bl_ver == 0) {
789f1ad8c93SVladimir Kondratiev 		le32_to_cpus(&bl.bl0.rf_type);
790f1ad8c93SVladimir Kondratiev 		le32_to_cpus(&bl.bl0.baseband_type);
791f1ad8c93SVladimir Kondratiev 		rf_status = 0; /* actually, unknown */
792f1ad8c93SVladimir Kondratiev 		wil_info(wil,
793f1ad8c93SVladimir Kondratiev 			 "Boot Loader struct v%d: MAC = %pM RF = 0x%08x bband = 0x%08x\n",
794f1ad8c93SVladimir Kondratiev 			 bl_ver, mac,
795f1ad8c93SVladimir Kondratiev 			 bl.bl0.rf_type, bl.bl0.baseband_type);
796f1ad8c93SVladimir Kondratiev 		wil_info(wil, "Boot Loader build unknown for struct v0\n");
79719c871ceSVladimir Kondratiev 	} else {
798f1ad8c93SVladimir Kondratiev 		le16_to_cpus(&bl.bl1.rf_type);
799f1ad8c93SVladimir Kondratiev 		rf_status = le16_to_cpu(bl.bl1.rf_status);
800f1ad8c93SVladimir Kondratiev 		le32_to_cpus(&bl.bl1.baseband_type);
801f1ad8c93SVladimir Kondratiev 		le16_to_cpus(&bl.bl1.bl_version_subminor);
802f1ad8c93SVladimir Kondratiev 		le16_to_cpus(&bl.bl1.bl_version_build);
803f1ad8c93SVladimir Kondratiev 		wil_info(wil,
804f1ad8c93SVladimir Kondratiev 			 "Boot Loader struct v%d: MAC = %pM RF = 0x%04x (status 0x%04x) bband = 0x%08x\n",
805f1ad8c93SVladimir Kondratiev 			 bl_ver, mac,
806f1ad8c93SVladimir Kondratiev 			 bl.bl1.rf_type, rf_status,
807f1ad8c93SVladimir Kondratiev 			 bl.bl1.baseband_type);
808f1ad8c93SVladimir Kondratiev 		wil_info(wil, "Boot Loader build %d.%d.%d.%d\n",
809f1ad8c93SVladimir Kondratiev 			 bl.bl1.bl_version_major, bl.bl1.bl_version_minor,
810f1ad8c93SVladimir Kondratiev 			 bl.bl1.bl_version_subminor, bl.bl1.bl_version_build);
8112cd0f021SVladimir Kondratiev 	}
8122cd0f021SVladimir Kondratiev 
813f1ad8c93SVladimir Kondratiev 	if (!is_valid_ether_addr(mac)) {
814f1ad8c93SVladimir Kondratiev 		wil_err(wil, "BL: Invalid MAC %pM\n", mac);
815f1ad8c93SVladimir Kondratiev 		return -EINVAL;
816f1ad8c93SVladimir Kondratiev 	}
817f1ad8c93SVladimir Kondratiev 
818f1ad8c93SVladimir Kondratiev 	ether_addr_copy(ndev->perm_addr, mac);
819b42f1196SHamad Kadmany 	ether_addr_copy(wiphy->perm_addr, mac);
8202cd0f021SVladimir Kondratiev 	if (!is_valid_ether_addr(ndev->dev_addr))
821f1ad8c93SVladimir Kondratiev 		ether_addr_copy(ndev->dev_addr, mac);
822f1ad8c93SVladimir Kondratiev 
823f1ad8c93SVladimir Kondratiev 	if (rf_status) {/* bad RF cable? */
824f1ad8c93SVladimir Kondratiev 		wil_err(wil, "RF communication error 0x%04x",
825f1ad8c93SVladimir Kondratiev 			rf_status);
826f1ad8c93SVladimir Kondratiev 		return -EAGAIN;
827f1ad8c93SVladimir Kondratiev 	}
8282cd0f021SVladimir Kondratiev 
8292cd0f021SVladimir Kondratiev 	return 0;
8302cd0f021SVladimir Kondratiev }
8312cd0f021SVladimir Kondratiev 
832409ead54SVladimir Kondratiev static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err)
833409ead54SVladimir Kondratiev {
834409ead54SVladimir Kondratiev 	u32 bl_assert_code, bl_assert_blink, bl_magic_number;
835409ead54SVladimir Kondratiev 	u32 bl_ver = wil_r(wil, RGF_USER_BL +
836409ead54SVladimir Kondratiev 			   offsetof(struct bl_dedicated_registers_v0,
837409ead54SVladimir Kondratiev 				    boot_loader_struct_version));
838409ead54SVladimir Kondratiev 
839409ead54SVladimir Kondratiev 	if (bl_ver < 2)
840409ead54SVladimir Kondratiev 		return;
841409ead54SVladimir Kondratiev 
842409ead54SVladimir Kondratiev 	bl_assert_code = wil_r(wil, RGF_USER_BL +
843409ead54SVladimir Kondratiev 			       offsetof(struct bl_dedicated_registers_v1,
844409ead54SVladimir Kondratiev 					bl_assert_code));
845409ead54SVladimir Kondratiev 	bl_assert_blink = wil_r(wil, RGF_USER_BL +
846409ead54SVladimir Kondratiev 				offsetof(struct bl_dedicated_registers_v1,
847409ead54SVladimir Kondratiev 					 bl_assert_blink));
848409ead54SVladimir Kondratiev 	bl_magic_number = wil_r(wil, RGF_USER_BL +
849409ead54SVladimir Kondratiev 				offsetof(struct bl_dedicated_registers_v1,
850409ead54SVladimir Kondratiev 					 bl_magic_number));
851409ead54SVladimir Kondratiev 
852409ead54SVladimir Kondratiev 	if (is_err) {
853409ead54SVladimir Kondratiev 		wil_err(wil,
854409ead54SVladimir Kondratiev 			"BL assert code 0x%08x blink 0x%08x magic 0x%08x\n",
855409ead54SVladimir Kondratiev 			bl_assert_code, bl_assert_blink, bl_magic_number);
856409ead54SVladimir Kondratiev 	} else {
857409ead54SVladimir Kondratiev 		wil_dbg_misc(wil,
858409ead54SVladimir Kondratiev 			     "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n",
859409ead54SVladimir Kondratiev 			     bl_assert_code, bl_assert_blink, bl_magic_number);
860409ead54SVladimir Kondratiev 	}
861409ead54SVladimir Kondratiev }
862409ead54SVladimir Kondratiev 
8632be7d22fSVladimir Kondratiev static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
8642be7d22fSVladimir Kondratiev {
8652be7d22fSVladimir Kondratiev 	ulong to = msecs_to_jiffies(1000);
8662be7d22fSVladimir Kondratiev 	ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
8678fe59627SVladimir Kondratiev 
8682be7d22fSVladimir Kondratiev 	if (0 == left) {
8692be7d22fSVladimir Kondratiev 		wil_err(wil, "Firmware not ready\n");
8702be7d22fSVladimir Kondratiev 		return -ETIME;
8712be7d22fSVladimir Kondratiev 	} else {
87215e23124SVladimir Kondratiev 		wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
87315e23124SVladimir Kondratiev 			 jiffies_to_msecs(to-left), wil->hw_version);
8742be7d22fSVladimir Kondratiev 	}
8752be7d22fSVladimir Kondratiev 	return 0;
8762be7d22fSVladimir Kondratiev }
8772be7d22fSVladimir Kondratiev 
878035859a5SMaya Erez void wil_abort_scan(struct wil6210_priv *wil, bool sync)
879035859a5SMaya Erez {
880035859a5SMaya Erez 	int rc;
881035859a5SMaya Erez 	struct cfg80211_scan_info info = {
882035859a5SMaya Erez 		.aborted = true,
883035859a5SMaya Erez 	};
884035859a5SMaya Erez 
885035859a5SMaya Erez 	lockdep_assert_held(&wil->p2p_wdev_mutex);
886035859a5SMaya Erez 
887035859a5SMaya Erez 	if (!wil->scan_request)
888035859a5SMaya Erez 		return;
889035859a5SMaya Erez 
890035859a5SMaya Erez 	wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request);
891035859a5SMaya Erez 	del_timer_sync(&wil->scan_timer);
892035859a5SMaya Erez 	mutex_unlock(&wil->p2p_wdev_mutex);
893035859a5SMaya Erez 	rc = wmi_abort_scan(wil);
894035859a5SMaya Erez 	if (!rc && sync)
895035859a5SMaya Erez 		wait_event_interruptible_timeout(wil->wq, !wil->scan_request,
896035859a5SMaya Erez 						 msecs_to_jiffies(
897035859a5SMaya Erez 						 WAIT_FOR_SCAN_ABORT_MS));
898035859a5SMaya Erez 
899035859a5SMaya Erez 	mutex_lock(&wil->p2p_wdev_mutex);
900035859a5SMaya Erez 	if (wil->scan_request) {
901035859a5SMaya Erez 		cfg80211_scan_done(wil->scan_request, &info);
902035859a5SMaya Erez 		wil->scan_request = NULL;
903035859a5SMaya Erez 	}
904035859a5SMaya Erez }
905035859a5SMaya Erez 
9062be7d22fSVladimir Kondratiev /*
9072be7d22fSVladimir Kondratiev  * We reset all the structures, and we reset the UMAC.
9082be7d22fSVladimir Kondratiev  * After calling this routine, you're expected to reload
9092be7d22fSVladimir Kondratiev  * the firmware.
9102be7d22fSVladimir Kondratiev  */
9112cd0f021SVladimir Kondratiev int wil_reset(struct wil6210_priv *wil, bool load_fw)
9122be7d22fSVladimir Kondratiev {
9132be7d22fSVladimir Kondratiev 	int rc;
9142be7d22fSVladimir Kondratiev 
915af3db60aSLazar Alexei 	wil_dbg_misc(wil, "reset\n");
9169cf10d62SVladimir Kondratiev 
917097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
9189419b6a2SVladimir Kondratiev 	WARN_ON(test_bit(wil_status_napi_en, wil->status));
919097638a0SVladimir Kondratiev 
920bfc2dc7aSVladimir Kondratiev 	if (debug_fw) {
921bfc2dc7aSVladimir Kondratiev 		static const u8 mac[ETH_ALEN] = {
922bfc2dc7aSVladimir Kondratiev 			0x00, 0xde, 0xad, 0x12, 0x34, 0x56,
923bfc2dc7aSVladimir Kondratiev 		};
924bfc2dc7aSVladimir Kondratiev 		struct net_device *ndev = wil_to_ndev(wil);
925bfc2dc7aSVladimir Kondratiev 
926bfc2dc7aSVladimir Kondratiev 		ether_addr_copy(ndev->perm_addr, mac);
927bfc2dc7aSVladimir Kondratiev 		ether_addr_copy(ndev->dev_addr, ndev->perm_addr);
928bfc2dc7aSVladimir Kondratiev 		return 0;
929bfc2dc7aSVladimir Kondratiev 	}
930bfc2dc7aSVladimir Kondratiev 
93167131a1dSVladimir Kondratiev 	if (wil->hw_version == HW_VER_UNKNOWN)
93267131a1dSVladimir Kondratiev 		return -ENODEV;
93367131a1dSVladimir Kondratiev 
9345f0823efSMaya Erez 	if (wil->platform_ops.notify) {
9355f0823efSMaya Erez 		rc = wil->platform_ops.notify(wil->platform_handle,
9365f0823efSMaya Erez 					      WIL_PLATFORM_EVT_PRE_RESET);
9375f0823efSMaya Erez 		if (rc)
938af3db60aSLazar Alexei 			wil_err(wil, "PRE_RESET platform notify failed, rc %d\n",
939af3db60aSLazar Alexei 				rc);
9405f0823efSMaya Erez 	}
9415f0823efSMaya Erez 
942f13e0630SHamad Kadmany 	set_bit(wil_status_resetting, wil->status);
943f13e0630SHamad Kadmany 
944097638a0SVladimir Kondratiev 	cancel_work_sync(&wil->disconnect_worker);
9454821e6d8SVladimir Kondratiev 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
94641d6b093SVladimir Kondratiev 	wil_bcast_fini(wil);
947097638a0SVladimir Kondratiev 
94810d599adSMaya Erez 	/* Disable device led before reset*/
94910d599adSMaya Erez 	wmi_led_cfg(wil, false);
95010d599adSMaya Erez 
95182e9f646SHamad Kadmany 	mutex_lock(&wil->p2p_wdev_mutex);
95282e9f646SHamad Kadmany 	wil_abort_scan(wil, false);
95382e9f646SHamad Kadmany 	mutex_unlock(&wil->p2p_wdev_mutex);
95482e9f646SHamad Kadmany 
955452133a7SMaya Erez 	/* prevent NAPI from being scheduled and prevent wmi commands */
956452133a7SMaya Erez 	mutex_lock(&wil->wmi_mutex);
9579419b6a2SVladimir Kondratiev 	bitmap_zero(wil->status, wil_status_last);
958452133a7SMaya Erez 	mutex_unlock(&wil->wmi_mutex);
9590fef1818SVladimir Kondratiev 
960e4dbb093SVladimir Kondratiev 	wil_mask_irq(wil);
9612be7d22fSVladimir Kondratiev 
962e08b5906SVladimir Kondratiev 	wmi_event_flush(wil);
963e08b5906SVladimir Kondratiev 
9643277213fSVladimir Kondratiev 	flush_workqueue(wil->wq_service);
965e08b5906SVladimir Kondratiev 	flush_workqueue(wil->wmi_wq);
966e08b5906SVladimir Kondratiev 
967409ead54SVladimir Kondratiev 	wil_bl_crash_info(wil, false);
9683ee908dcSHamad Kadmany 	wil_disable_irq(wil);
969bbb2adc7SVladimir Kondratiev 	rc = wil_target_reset(wil);
9703ee908dcSHamad Kadmany 	wil6210_clear_irq(wil);
9713ee908dcSHamad Kadmany 	wil_enable_irq(wil);
9728bf6adb9SVladimir Kondratiev 	wil_rx_fini(wil);
973409ead54SVladimir Kondratiev 	if (rc) {
974409ead54SVladimir Kondratiev 		wil_bl_crash_info(wil, true);
975bbb2adc7SVladimir Kondratiev 		return rc;
976409ead54SVladimir Kondratiev 	}
977bbb2adc7SVladimir Kondratiev 
9782cd0f021SVladimir Kondratiev 	rc = wil_get_bl_info(wil);
979f1ad8c93SVladimir Kondratiev 	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
980f1ad8c93SVladimir Kondratiev 		rc = 0;
9812cd0f021SVladimir Kondratiev 	if (rc)
9822cd0f021SVladimir Kondratiev 		return rc;
9832cd0f021SVladimir Kondratiev 
9841f1a361aSLior David 	wil_set_oob_mode(wil, oob_mode);
9852cd0f021SVladimir Kondratiev 	if (load_fw) {
986a351f2f5SLazar Alexei 		wil_info(wil, "Use firmware <%s> + board <%s>\n",
987a351f2f5SLazar Alexei 			 wil->wil_fw_name, WIL_BOARD_FILE_NAME);
9882cd0f021SVladimir Kondratiev 
989151a9706SVladimir Kondratiev 		wil_halt_cpu(wil);
99013cd9f75SLior David 		memset(wil->fw_version, 0, sizeof(wil->fw_version));
991151a9706SVladimir Kondratiev 		/* Loading f/w from the file */
992a351f2f5SLazar Alexei 		rc = wil_request_firmware(wil, wil->wil_fw_name, true);
993151a9706SVladimir Kondratiev 		if (rc)
994151a9706SVladimir Kondratiev 			return rc;
995a351f2f5SLazar Alexei 		rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
9962cd0f021SVladimir Kondratiev 		if (rc)
9972cd0f021SVladimir Kondratiev 			return rc;
998151a9706SVladimir Kondratiev 
9992cd0f021SVladimir Kondratiev 		/* Mark FW as loaded from host */
1000b9eeb512SVladimir Kondratiev 		wil_s(wil, RGF_USER_USAGE_6, 1);
10012cd0f021SVladimir Kondratiev 
10022cd0f021SVladimir Kondratiev 		/* clear any interrupts which on-card-firmware
10032cd0f021SVladimir Kondratiev 		 * may have set
10042cd0f021SVladimir Kondratiev 		 */
1005151a9706SVladimir Kondratiev 		wil6210_clear_irq(wil);
10062cd0f021SVladimir Kondratiev 		/* CAF_ICR - clear and mask */
10072cd0f021SVladimir Kondratiev 		/* it is W1C, clear by writing back same value */
1008b9eeb512SVladimir Kondratiev 		wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
1009b9eeb512SVladimir Kondratiev 		wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
1010151a9706SVladimir Kondratiev 
1011151a9706SVladimir Kondratiev 		wil_release_cpu(wil);
1012151a9706SVladimir Kondratiev 	}
10138bf6adb9SVladimir Kondratiev 
10142be7d22fSVladimir Kondratiev 	/* init after reset */
101502beaf1aSVladimir Kondratiev 	wil->ap_isolate = 0;
101616735d02SWolfram Sang 	reinit_completion(&wil->wmi_ready);
101759502647SDedy Lansky 	reinit_completion(&wil->wmi_call);
1018349214c1SMaya Erez 	reinit_completion(&wil->halp.comp);
10192be7d22fSVladimir Kondratiev 
10202cd0f021SVladimir Kondratiev 	if (load_fw) {
102178366f69SVladimir Kondratiev 		wil_configure_interrupt_moderation(wil);
1022e4dbb093SVladimir Kondratiev 		wil_unmask_irq(wil);
10232be7d22fSVladimir Kondratiev 
10242be7d22fSVladimir Kondratiev 		/* we just started MAC, wait for FW ready */
10252be7d22fSVladimir Kondratiev 		rc = wil_wait_for_fw_ready(wil);
10265f0823efSMaya Erez 		if (rc)
10275f0823efSMaya Erez 			return rc;
10285f0823efSMaya Erez 
10295f0823efSMaya Erez 		/* check FW is responsive */
1030cec94d8cSVladimir Kondratiev 		rc = wmi_echo(wil);
10315f0823efSMaya Erez 		if (rc) {
1032af3db60aSLazar Alexei 			wil_err(wil, "wmi_echo failed, rc %d\n", rc);
10335f0823efSMaya Erez 			return rc;
10345f0823efSMaya Erez 		}
10355f0823efSMaya Erez 
10363fea18d0SLior David 		wil_collect_fw_info(wil);
10373fea18d0SLior David 
10385f0823efSMaya Erez 		if (wil->platform_ops.notify) {
10395f0823efSMaya Erez 			rc = wil->platform_ops.notify(wil->platform_handle,
10405f0823efSMaya Erez 						      WIL_PLATFORM_EVT_FW_RDY);
10415f0823efSMaya Erez 			if (rc) {
1042af3db60aSLazar Alexei 				wil_err(wil, "FW_RDY notify failed, rc %d\n",
1043af3db60aSLazar Alexei 					rc);
10445f0823efSMaya Erez 				rc = 0;
10455f0823efSMaya Erez 			}
10465f0823efSMaya Erez 		}
10472cd0f021SVladimir Kondratiev 	}
10482be7d22fSVladimir Kondratiev 
10492be7d22fSVladimir Kondratiev 	return rc;
10502be7d22fSVladimir Kondratiev }
10512be7d22fSVladimir Kondratiev 
1052ed6f9dc6SVladimir Kondratiev void wil_fw_error_recovery(struct wil6210_priv *wil)
1053ed6f9dc6SVladimir Kondratiev {
1054ed6f9dc6SVladimir Kondratiev 	wil_dbg_misc(wil, "starting fw error recovery\n");
1055f13e0630SHamad Kadmany 
1056f13e0630SHamad Kadmany 	if (test_bit(wil_status_resetting, wil->status)) {
1057f13e0630SHamad Kadmany 		wil_info(wil, "Reset already in progress\n");
1058f13e0630SHamad Kadmany 		return;
1059f13e0630SHamad Kadmany 	}
1060f13e0630SHamad Kadmany 
1061c33407a8SVladimir Kondratiev 	wil->recovery_state = fw_recovery_pending;
1062ed6f9dc6SVladimir Kondratiev 	schedule_work(&wil->fw_error_worker);
1063ed6f9dc6SVladimir Kondratiev }
10642be7d22fSVladimir Kondratiev 
106573d839aeSVladimir Kondratiev int __wil_up(struct wil6210_priv *wil)
10662be7d22fSVladimir Kondratiev {
10672be7d22fSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
10682be7d22fSVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
10692be7d22fSVladimir Kondratiev 	int rc;
10702be7d22fSVladimir Kondratiev 
1071097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
1072097638a0SVladimir Kondratiev 
10732cd0f021SVladimir Kondratiev 	rc = wil_reset(wil, true);
10742be7d22fSVladimir Kondratiev 	if (rc)
10752be7d22fSVladimir Kondratiev 		return rc;
10762be7d22fSVladimir Kondratiev 
1077e31b2562SVladimir Kondratiev 	/* Rx VRING. After MAC and beacon */
1078d3762b40SVladimir Kondratiev 	rc = wil_rx_init(wil, 1 << rx_ring_order);
1079e31b2562SVladimir Kondratiev 	if (rc)
1080e31b2562SVladimir Kondratiev 		return rc;
1081e31b2562SVladimir Kondratiev 
10822be7d22fSVladimir Kondratiev 	switch (wdev->iftype) {
10832be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
10847743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: STATION\n");
10852be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
10862be7d22fSVladimir Kondratiev 		break;
10872be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_AP:
10887743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: AP\n");
10892be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
10902be7d22fSVladimir Kondratiev 		break;
10912be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
10927743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: P2P_CLIENT\n");
10932be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
10942be7d22fSVladimir Kondratiev 		break;
10952be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
10967743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: P2P_GO\n");
10972be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
10982be7d22fSVladimir Kondratiev 		break;
10992be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
11007743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: Monitor\n");
11012be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_IEEE80211_RADIOTAP;
11022be7d22fSVladimir Kondratiev 		/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
11032be7d22fSVladimir Kondratiev 		break;
11042be7d22fSVladimir Kondratiev 	default:
11052be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
11062be7d22fSVladimir Kondratiev 	}
11072be7d22fSVladimir Kondratiev 
11082be7d22fSVladimir Kondratiev 	/* MAC address - pre-requisite for other commands */
11092be7d22fSVladimir Kondratiev 	wmi_set_mac_address(wil, ndev->dev_addr);
11102be7d22fSVladimir Kondratiev 
111173d839aeSVladimir Kondratiev 	wil_dbg_misc(wil, "NAPI enable\n");
1112e0287c4aSVladimir Kondratiev 	napi_enable(&wil->napi_rx);
1113e0287c4aSVladimir Kondratiev 	napi_enable(&wil->napi_tx);
11149419b6a2SVladimir Kondratiev 	set_bit(wil_status_napi_en, wil->status);
1115e0287c4aSVladimir Kondratiev 
11169953a782SLior David 	wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
1117f772ebfbSVladimir Kondratiev 
11182be7d22fSVladimir Kondratiev 	return 0;
11192be7d22fSVladimir Kondratiev }
11202be7d22fSVladimir Kondratiev 
11212be7d22fSVladimir Kondratiev int wil_up(struct wil6210_priv *wil)
11222be7d22fSVladimir Kondratiev {
11232be7d22fSVladimir Kondratiev 	int rc;
11242be7d22fSVladimir Kondratiev 
1125af3db60aSLazar Alexei 	wil_dbg_misc(wil, "up\n");
11269cf10d62SVladimir Kondratiev 
11272be7d22fSVladimir Kondratiev 	mutex_lock(&wil->mutex);
11282be7d22fSVladimir Kondratiev 	rc = __wil_up(wil);
11292be7d22fSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
11302be7d22fSVladimir Kondratiev 
11312be7d22fSVladimir Kondratiev 	return rc;
11322be7d22fSVladimir Kondratiev }
11332be7d22fSVladimir Kondratiev 
113473d839aeSVladimir Kondratiev int __wil_down(struct wil6210_priv *wil)
11352be7d22fSVladimir Kondratiev {
1136097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
1137097638a0SVladimir Kondratiev 
1138b0c0e688SLazar Alexei 	set_bit(wil_status_resetting, wil->status);
1139b0c0e688SLazar Alexei 
11409953a782SLior David 	wil6210_bus_request(wil, 0);
1141f772ebfbSVladimir Kondratiev 
114273d839aeSVladimir Kondratiev 	wil_disable_irq(wil);
11439419b6a2SVladimir Kondratiev 	if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
1144e0287c4aSVladimir Kondratiev 		napi_disable(&wil->napi_rx);
1145e0287c4aSVladimir Kondratiev 		napi_disable(&wil->napi_tx);
114673d839aeSVladimir Kondratiev 		wil_dbg_misc(wil, "NAPI disable\n");
114773d839aeSVladimir Kondratiev 	}
114873d839aeSVladimir Kondratiev 	wil_enable_irq(wil);
1149e0287c4aSVladimir Kondratiev 
11505ffae432SLior David 	mutex_lock(&wil->p2p_wdev_mutex);
1151035859a5SMaya Erez 	wil_p2p_stop_radio_operations(wil);
1152035859a5SMaya Erez 	wil_abort_scan(wil, false);
11535ffae432SLior David 	mutex_unlock(&wil->p2p_wdev_mutex);
11542be7d22fSVladimir Kondratiev 
11552cd0f021SVladimir Kondratiev 	wil_reset(wil, false);
11562be7d22fSVladimir Kondratiev 
11572be7d22fSVladimir Kondratiev 	return 0;
11582be7d22fSVladimir Kondratiev }
11592be7d22fSVladimir Kondratiev 
11602be7d22fSVladimir Kondratiev int wil_down(struct wil6210_priv *wil)
11612be7d22fSVladimir Kondratiev {
11622be7d22fSVladimir Kondratiev 	int rc;
11632be7d22fSVladimir Kondratiev 
1164af3db60aSLazar Alexei 	wil_dbg_misc(wil, "down\n");
11659cf10d62SVladimir Kondratiev 
1166c33407a8SVladimir Kondratiev 	wil_set_recovery_state(wil, fw_recovery_idle);
11672be7d22fSVladimir Kondratiev 	mutex_lock(&wil->mutex);
11682be7d22fSVladimir Kondratiev 	rc = __wil_down(wil);
11692be7d22fSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
11702be7d22fSVladimir Kondratiev 
11712be7d22fSVladimir Kondratiev 	return rc;
11722be7d22fSVladimir Kondratiev }
11733df2cd36SVladimir Kondratiev 
11743df2cd36SVladimir Kondratiev int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
11753df2cd36SVladimir Kondratiev {
11763df2cd36SVladimir Kondratiev 	int i;
11773df2cd36SVladimir Kondratiev 	int rc = -ENOENT;
11783df2cd36SVladimir Kondratiev 
11793df2cd36SVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
11803df2cd36SVladimir Kondratiev 		if ((wil->sta[i].status != wil_sta_unused) &&
1181108d1eb6SVladimir Kondratiev 		    ether_addr_equal(wil->sta[i].addr, mac)) {
11823df2cd36SVladimir Kondratiev 			rc = i;
11833df2cd36SVladimir Kondratiev 			break;
11843df2cd36SVladimir Kondratiev 		}
11853df2cd36SVladimir Kondratiev 	}
11863df2cd36SVladimir Kondratiev 
11873df2cd36SVladimir Kondratiev 	return rc;
11883df2cd36SVladimir Kondratiev }
1189349214c1SMaya Erez 
1190349214c1SMaya Erez void wil_halp_vote(struct wil6210_priv *wil)
1191349214c1SMaya Erez {
1192349214c1SMaya Erez 	unsigned long rc;
1193349214c1SMaya Erez 	unsigned long to_jiffies = msecs_to_jiffies(WAIT_FOR_HALP_VOTE_MS);
1194349214c1SMaya Erez 
1195349214c1SMaya Erez 	mutex_lock(&wil->halp.lock);
1196349214c1SMaya Erez 
1197af3db60aSLazar Alexei 	wil_dbg_irq(wil, "halp_vote: start, HALP ref_cnt (%d)\n",
1198349214c1SMaya Erez 		    wil->halp.ref_cnt);
1199349214c1SMaya Erez 
1200349214c1SMaya Erez 	if (++wil->halp.ref_cnt == 1) {
120118618a9fSMaya Erez 		reinit_completion(&wil->halp.comp);
1202349214c1SMaya Erez 		wil6210_set_halp(wil);
1203349214c1SMaya Erez 		rc = wait_for_completion_timeout(&wil->halp.comp, to_jiffies);
1204f1b7764fSMaya Erez 		if (!rc) {
1205af3db60aSLazar Alexei 			wil_err(wil, "HALP vote timed out\n");
1206f1b7764fSMaya Erez 			/* Mask HALP as done in case the interrupt is raised */
1207f1b7764fSMaya Erez 			wil6210_mask_halp(wil);
1208f1b7764fSMaya Erez 		} else {
1209ef86f249SLior David 			wil_dbg_irq(wil,
1210af3db60aSLazar Alexei 				    "halp_vote: HALP vote completed after %d ms\n",
1211349214c1SMaya Erez 				    jiffies_to_msecs(to_jiffies - rc));
1212349214c1SMaya Erez 		}
1213f1b7764fSMaya Erez 	}
1214349214c1SMaya Erez 
1215af3db60aSLazar Alexei 	wil_dbg_irq(wil, "halp_vote: end, HALP ref_cnt (%d)\n",
1216349214c1SMaya Erez 		    wil->halp.ref_cnt);
1217349214c1SMaya Erez 
1218349214c1SMaya Erez 	mutex_unlock(&wil->halp.lock);
1219349214c1SMaya Erez }
1220349214c1SMaya Erez 
1221349214c1SMaya Erez void wil_halp_unvote(struct wil6210_priv *wil)
1222349214c1SMaya Erez {
1223349214c1SMaya Erez 	WARN_ON(wil->halp.ref_cnt == 0);
1224349214c1SMaya Erez 
1225349214c1SMaya Erez 	mutex_lock(&wil->halp.lock);
1226349214c1SMaya Erez 
1227af3db60aSLazar Alexei 	wil_dbg_irq(wil, "halp_unvote: start, HALP ref_cnt (%d)\n",
1228349214c1SMaya Erez 		    wil->halp.ref_cnt);
1229349214c1SMaya Erez 
1230349214c1SMaya Erez 	if (--wil->halp.ref_cnt == 0) {
1231349214c1SMaya Erez 		wil6210_clear_halp(wil);
1232af3db60aSLazar Alexei 		wil_dbg_irq(wil, "HALP unvote\n");
1233349214c1SMaya Erez 	}
1234349214c1SMaya Erez 
1235af3db60aSLazar Alexei 	wil_dbg_irq(wil, "halp_unvote:end, HALP ref_cnt (%d)\n",
1236349214c1SMaya Erez 		    wil->halp.ref_cnt);
1237349214c1SMaya Erez 
1238349214c1SMaya Erez 	mutex_unlock(&wil->halp.lock);
1239349214c1SMaya Erez }
1240