12be7d22fSVladimir Kondratiev /*
22be7d22fSVladimir Kondratiev  * Copyright (c) 2012 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"
232be7d22fSVladimir Kondratiev 
24ed6f9dc6SVladimir Kondratiev static bool no_fw_recovery;
25ed6f9dc6SVladimir Kondratiev module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
26ed6f9dc6SVladimir Kondratiev MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
27ed6f9dc6SVladimir Kondratiev 
282be7d22fSVladimir Kondratiev /*
292be7d22fSVladimir Kondratiev  * Due to a hardware issue,
302be7d22fSVladimir Kondratiev  * one has to read/write to/from NIC in 32-bit chunks;
312be7d22fSVladimir Kondratiev  * regular memcpy_fromio and siblings will
322be7d22fSVladimir Kondratiev  * not work on 64-bit platform - it uses 64-bit transactions
332be7d22fSVladimir Kondratiev  *
342be7d22fSVladimir Kondratiev  * Force 32-bit transactions to enable NIC on 64-bit platforms
352be7d22fSVladimir Kondratiev  *
362be7d22fSVladimir Kondratiev  * To avoid byte swap on big endian host, __raw_{read|write}l
372be7d22fSVladimir Kondratiev  * should be used - {read|write}l would swap bytes to provide
382be7d22fSVladimir Kondratiev  * little endian on PCI value in host endianness.
392be7d22fSVladimir Kondratiev  */
402be7d22fSVladimir Kondratiev void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
412be7d22fSVladimir Kondratiev 			  size_t count)
422be7d22fSVladimir Kondratiev {
432be7d22fSVladimir Kondratiev 	u32 *d = dst;
442be7d22fSVladimir Kondratiev 	const volatile u32 __iomem *s = src;
452be7d22fSVladimir Kondratiev 
462be7d22fSVladimir Kondratiev 	/* size_t is unsigned, if (count%4 != 0) it will wrap */
472be7d22fSVladimir Kondratiev 	for (count += 4; count > 4; count -= 4)
482be7d22fSVladimir Kondratiev 		*d++ = __raw_readl(s++);
492be7d22fSVladimir Kondratiev }
502be7d22fSVladimir Kondratiev 
512be7d22fSVladimir Kondratiev void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
522be7d22fSVladimir Kondratiev 			size_t count)
532be7d22fSVladimir Kondratiev {
542be7d22fSVladimir Kondratiev 	volatile u32 __iomem *d = dst;
552be7d22fSVladimir Kondratiev 	const u32 *s = src;
562be7d22fSVladimir Kondratiev 
572be7d22fSVladimir Kondratiev 	for (count += 4; count > 4; count -= 4)
582be7d22fSVladimir Kondratiev 		__raw_writel(*s++, d++);
592be7d22fSVladimir Kondratiev }
602be7d22fSVladimir Kondratiev 
6191886b0bSVladimir Kondratiev static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
622be7d22fSVladimir Kondratiev {
6391886b0bSVladimir Kondratiev 	uint i;
64b4490f42SVladimir Kondratiev 	struct wil_sta_info *sta = &wil->sta[cid];
654d55a0a1SVladimir Kondratiev 
66e58c9f70SVladimir Kondratiev 	sta->data_port_open = false;
674d55a0a1SVladimir Kondratiev 	if (sta->status != wil_sta_unused) {
684d55a0a1SVladimir Kondratiev 		wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
694d55a0a1SVladimir Kondratiev 		sta->status = wil_sta_unused;
704d55a0a1SVladimir Kondratiev 	}
714d55a0a1SVladimir Kondratiev 
72b4490f42SVladimir Kondratiev 	for (i = 0; i < WIL_STA_TID_NUM; i++) {
73b4490f42SVladimir Kondratiev 		struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
74b4490f42SVladimir Kondratiev 		sta->tid_rx[i] = NULL;
75b4490f42SVladimir Kondratiev 		wil_tid_ampdu_rx_free(wil, r);
76b4490f42SVladimir Kondratiev 	}
7791886b0bSVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
7891886b0bSVladimir Kondratiev 		if (wil->vring2cid_tid[i][0] == cid)
7991886b0bSVladimir Kondratiev 			wil_vring_fini_tx(wil, i);
8091886b0bSVladimir Kondratiev 	}
8191886b0bSVladimir Kondratiev 	memset(&sta->stats, 0, sizeof(sta->stats));
82b4490f42SVladimir Kondratiev }
83b4490f42SVladimir Kondratiev 
843b3a0162SJohannes Berg static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
8591886b0bSVladimir Kondratiev {
8691886b0bSVladimir Kondratiev 	int cid = -ENOENT;
8791886b0bSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
8891886b0bSVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
8991886b0bSVladimir Kondratiev 
9091886b0bSVladimir Kondratiev 	might_sleep();
9191886b0bSVladimir Kondratiev 	if (bssid) {
9291886b0bSVladimir Kondratiev 		cid = wil_find_cid(wil, bssid);
9391886b0bSVladimir Kondratiev 		wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid);
9491886b0bSVladimir Kondratiev 	} else {
9591886b0bSVladimir Kondratiev 		wil_dbg_misc(wil, "%s(all)\n", __func__);
9691886b0bSVladimir Kondratiev 	}
9791886b0bSVladimir Kondratiev 
9891886b0bSVladimir Kondratiev 	if (cid >= 0) /* disconnect 1 peer */
9991886b0bSVladimir Kondratiev 		wil_disconnect_cid(wil, cid);
10091886b0bSVladimir Kondratiev 	else /* disconnect all */
10191886b0bSVladimir Kondratiev 		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
10291886b0bSVladimir Kondratiev 			wil_disconnect_cid(wil, cid);
10391886b0bSVladimir Kondratiev 
10491886b0bSVladimir Kondratiev 	/* link state */
10591886b0bSVladimir Kondratiev 	switch (wdev->iftype) {
10691886b0bSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
10791886b0bSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
1082be7d22fSVladimir Kondratiev 		wil_link_off(wil);
109b338f74eSVladimir Kondratiev 		if (test_bit(wil_status_fwconnected, &wil->status)) {
1102be7d22fSVladimir Kondratiev 			clear_bit(wil_status_fwconnected, &wil->status);
111b338f74eSVladimir Kondratiev 			cfg80211_disconnected(ndev,
112b338f74eSVladimir Kondratiev 					      WLAN_STATUS_UNSPECIFIED_FAILURE,
1132be7d22fSVladimir Kondratiev 					      NULL, 0, GFP_KERNEL);
114b338f74eSVladimir Kondratiev 		} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
1152be7d22fSVladimir Kondratiev 			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
1162be7d22fSVladimir Kondratiev 						WLAN_STATUS_UNSPECIFIED_FAILURE,
1172be7d22fSVladimir Kondratiev 						GFP_KERNEL);
1182be7d22fSVladimir Kondratiev 		}
119b338f74eSVladimir Kondratiev 		clear_bit(wil_status_fwconnecting, &wil->status);
12091886b0bSVladimir Kondratiev 		break;
12191886b0bSVladimir Kondratiev 	default:
12291886b0bSVladimir Kondratiev 		/* AP-like interface and monitor:
12391886b0bSVladimir Kondratiev 		 * never scan, always connected
12491886b0bSVladimir Kondratiev 		 */
12591886b0bSVladimir Kondratiev 		if (bssid)
12691886b0bSVladimir Kondratiev 			cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
12791886b0bSVladimir Kondratiev 		break;
12891886b0bSVladimir Kondratiev 	}
1292be7d22fSVladimir Kondratiev }
1302be7d22fSVladimir Kondratiev 
1312be7d22fSVladimir Kondratiev static void wil_disconnect_worker(struct work_struct *work)
1322be7d22fSVladimir Kondratiev {
1332be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = container_of(work,
1342be7d22fSVladimir Kondratiev 			struct wil6210_priv, disconnect_worker);
1352be7d22fSVladimir Kondratiev 
136097638a0SVladimir Kondratiev 	mutex_lock(&wil->mutex);
1372be7d22fSVladimir Kondratiev 	_wil6210_disconnect(wil, NULL);
138097638a0SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
1392be7d22fSVladimir Kondratiev }
1402be7d22fSVladimir Kondratiev 
1412be7d22fSVladimir Kondratiev static void wil_connect_timer_fn(ulong x)
1422be7d22fSVladimir Kondratiev {
1432be7d22fSVladimir Kondratiev 	struct wil6210_priv *wil = (void *)x;
1442be7d22fSVladimir Kondratiev 
1457743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "Connect timeout\n");
1462be7d22fSVladimir Kondratiev 
1472be7d22fSVladimir Kondratiev 	/* reschedule to thread context - disconnect won't
1482be7d22fSVladimir Kondratiev 	 * run from atomic context
1492be7d22fSVladimir Kondratiev 	 */
1502be7d22fSVladimir Kondratiev 	schedule_work(&wil->disconnect_worker);
1512be7d22fSVladimir Kondratiev }
1522be7d22fSVladimir Kondratiev 
153ed6f9dc6SVladimir Kondratiev static void wil_fw_error_worker(struct work_struct *work)
154ed6f9dc6SVladimir Kondratiev {
155ed6f9dc6SVladimir Kondratiev 	struct wil6210_priv *wil = container_of(work,
156ed6f9dc6SVladimir Kondratiev 			struct wil6210_priv, fw_error_worker);
157ed6f9dc6SVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
158ed6f9dc6SVladimir Kondratiev 
159ed6f9dc6SVladimir Kondratiev 	wil_dbg_misc(wil, "fw error worker\n");
160ed6f9dc6SVladimir Kondratiev 
161ed6f9dc6SVladimir Kondratiev 	if (no_fw_recovery)
162ed6f9dc6SVladimir Kondratiev 		return;
163ed6f9dc6SVladimir Kondratiev 
164fc219eedSVladimir Kondratiev 	/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
165fc219eedSVladimir Kondratiev 	 * passed since last recovery attempt
166fc219eedSVladimir Kondratiev 	 */
167fc219eedSVladimir Kondratiev 	if (time_is_after_jiffies(wil->last_fw_recovery +
168fc219eedSVladimir Kondratiev 				  WIL6210_FW_RECOVERY_TO))
169fc219eedSVladimir Kondratiev 		wil->recovery_count++;
170fc219eedSVladimir Kondratiev 	else
171fc219eedSVladimir Kondratiev 		wil->recovery_count = 1; /* fw was alive for a long time */
172fc219eedSVladimir Kondratiev 
173fc219eedSVladimir Kondratiev 	if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
174fc219eedSVladimir Kondratiev 		wil_err(wil, "too many recovery attempts (%d), giving up\n",
175fc219eedSVladimir Kondratiev 			wil->recovery_count);
176fc219eedSVladimir Kondratiev 		return;
177fc219eedSVladimir Kondratiev 	}
178fc219eedSVladimir Kondratiev 
179fc219eedSVladimir Kondratiev 	wil->last_fw_recovery = jiffies;
180fc219eedSVladimir Kondratiev 
1819c3bde56SVladimir Kondratiev 	mutex_lock(&wil->mutex);
182ed6f9dc6SVladimir Kondratiev 	switch (wdev->iftype) {
183ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
184ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
185ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
186fc219eedSVladimir Kondratiev 		wil_info(wil, "fw error recovery started (try %d)...\n",
187fc219eedSVladimir Kondratiev 			 wil->recovery_count);
188ed6f9dc6SVladimir Kondratiev 		wil_reset(wil);
189ed6f9dc6SVladimir Kondratiev 
190ed6f9dc6SVladimir Kondratiev 		/* need to re-allocate Rx ring after reset */
191ed6f9dc6SVladimir Kondratiev 		wil_rx_init(wil);
192ed6f9dc6SVladimir Kondratiev 		break;
193ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_AP:
194ed6f9dc6SVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
195ed6f9dc6SVladimir Kondratiev 		/* recovery in these modes is done by upper layers */
196ed6f9dc6SVladimir Kondratiev 		break;
197ed6f9dc6SVladimir Kondratiev 	default:
198ed6f9dc6SVladimir Kondratiev 		break;
199ed6f9dc6SVladimir Kondratiev 	}
2009c3bde56SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
201ed6f9dc6SVladimir Kondratiev }
202ed6f9dc6SVladimir Kondratiev 
2039a177384SVladimir Kondratiev static int wil_find_free_vring(struct wil6210_priv *wil)
2049a177384SVladimir Kondratiev {
2059a177384SVladimir Kondratiev 	int i;
2069a177384SVladimir Kondratiev 	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
2079a177384SVladimir Kondratiev 		if (!wil->vring_tx[i].va)
2089a177384SVladimir Kondratiev 			return i;
2099a177384SVladimir Kondratiev 	}
2109a177384SVladimir Kondratiev 	return -EINVAL;
2119a177384SVladimir Kondratiev }
2129a177384SVladimir Kondratiev 
213d81079f1SVladimir Kondratiev static void wil_connect_worker(struct work_struct *work)
214d81079f1SVladimir Kondratiev {
215d81079f1SVladimir Kondratiev 	int rc;
216d81079f1SVladimir Kondratiev 	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
217d81079f1SVladimir Kondratiev 						connect_worker);
218d81079f1SVladimir Kondratiev 	int cid = wil->pending_connect_cid;
2199a177384SVladimir Kondratiev 	int ringid = wil_find_free_vring(wil);
220d81079f1SVladimir Kondratiev 
221d81079f1SVladimir Kondratiev 	if (cid < 0) {
222d81079f1SVladimir Kondratiev 		wil_err(wil, "No connection pending\n");
223d81079f1SVladimir Kondratiev 		return;
224d81079f1SVladimir Kondratiev 	}
225d81079f1SVladimir Kondratiev 
226d81079f1SVladimir Kondratiev 	wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
227d81079f1SVladimir Kondratiev 
2289a177384SVladimir Kondratiev 	rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
229d81079f1SVladimir Kondratiev 	wil->pending_connect_cid = -1;
2303df2cd36SVladimir Kondratiev 	if (rc == 0) {
2313df2cd36SVladimir Kondratiev 		wil->sta[cid].status = wil_sta_connected;
232d81079f1SVladimir Kondratiev 		wil_link_on(wil);
2333df2cd36SVladimir Kondratiev 	} else {
2343df2cd36SVladimir Kondratiev 		wil->sta[cid].status = wil_sta_unused;
2353df2cd36SVladimir Kondratiev 	}
236d81079f1SVladimir Kondratiev }
237d81079f1SVladimir Kondratiev 
2382be7d22fSVladimir Kondratiev int wil_priv_init(struct wil6210_priv *wil)
2392be7d22fSVladimir Kondratiev {
2407743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "%s()\n", __func__);
2412be7d22fSVladimir Kondratiev 
2423df2cd36SVladimir Kondratiev 	memset(wil->sta, 0, sizeof(wil->sta));
2433df2cd36SVladimir Kondratiev 
2442be7d22fSVladimir Kondratiev 	mutex_init(&wil->mutex);
2452be7d22fSVladimir Kondratiev 	mutex_init(&wil->wmi_mutex);
2462be7d22fSVladimir Kondratiev 
2472be7d22fSVladimir Kondratiev 	init_completion(&wil->wmi_ready);
2482be7d22fSVladimir Kondratiev 
2492be7d22fSVladimir Kondratiev 	wil->pending_connect_cid = -1;
2502be7d22fSVladimir Kondratiev 	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
2512be7d22fSVladimir Kondratiev 
252d81079f1SVladimir Kondratiev 	INIT_WORK(&wil->connect_worker, wil_connect_worker);
2532be7d22fSVladimir Kondratiev 	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
2542be7d22fSVladimir Kondratiev 	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
255ed6f9dc6SVladimir Kondratiev 	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
2562be7d22fSVladimir Kondratiev 
2572be7d22fSVladimir Kondratiev 	INIT_LIST_HEAD(&wil->pending_wmi_ev);
2582be7d22fSVladimir Kondratiev 	spin_lock_init(&wil->wmi_ev_lock);
2592be7d22fSVladimir Kondratiev 
2602be7d22fSVladimir Kondratiev 	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
2612be7d22fSVladimir Kondratiev 	if (!wil->wmi_wq)
2622be7d22fSVladimir Kondratiev 		return -EAGAIN;
2632be7d22fSVladimir Kondratiev 
2642be7d22fSVladimir Kondratiev 	wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect");
2652be7d22fSVladimir Kondratiev 	if (!wil->wmi_wq_conn) {
2662be7d22fSVladimir Kondratiev 		destroy_workqueue(wil->wmi_wq);
2672be7d22fSVladimir Kondratiev 		return -EAGAIN;
2682be7d22fSVladimir Kondratiev 	}
2692be7d22fSVladimir Kondratiev 
270fc219eedSVladimir Kondratiev 	wil->last_fw_recovery = jiffies;
271fc219eedSVladimir Kondratiev 
2722be7d22fSVladimir Kondratiev 	return 0;
2732be7d22fSVladimir Kondratiev }
2742be7d22fSVladimir Kondratiev 
2753b3a0162SJohannes Berg void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
2762be7d22fSVladimir Kondratiev {
2772be7d22fSVladimir Kondratiev 	del_timer_sync(&wil->connect_timer);
2782be7d22fSVladimir Kondratiev 	_wil6210_disconnect(wil, bssid);
2792be7d22fSVladimir Kondratiev }
2802be7d22fSVladimir Kondratiev 
2812be7d22fSVladimir Kondratiev void wil_priv_deinit(struct wil6210_priv *wil)
2822be7d22fSVladimir Kondratiev {
2832be7d22fSVladimir Kondratiev 	cancel_work_sync(&wil->disconnect_worker);
284ed6f9dc6SVladimir Kondratiev 	cancel_work_sync(&wil->fw_error_worker);
285097638a0SVladimir Kondratiev 	mutex_lock(&wil->mutex);
2862be7d22fSVladimir Kondratiev 	wil6210_disconnect(wil, NULL);
287097638a0SVladimir Kondratiev 	mutex_unlock(&wil->mutex);
2882be7d22fSVladimir Kondratiev 	wmi_event_flush(wil);
2892be7d22fSVladimir Kondratiev 	destroy_workqueue(wil->wmi_wq_conn);
2902be7d22fSVladimir Kondratiev 	destroy_workqueue(wil->wmi_wq);
2912be7d22fSVladimir Kondratiev }
2922be7d22fSVladimir Kondratiev 
2932be7d22fSVladimir Kondratiev static void wil_target_reset(struct wil6210_priv *wil)
2942be7d22fSVladimir Kondratiev {
29598a65b59SVladimir Kondratiev 	int delay = 0;
296d28bcc30SVladimir Kondratiev 	u32 hw_state;
29736b10a72SVladimir Kondratiev 	u32 rev_id;
29836b10a72SVladimir Kondratiev 
2997743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "Resetting...\n");
3002be7d22fSVladimir Kondratiev 
30136b10a72SVladimir Kondratiev 	/* register read */
30236b10a72SVladimir Kondratiev #define R(a) ioread32(wil->csr + HOSTADDR(a))
3032be7d22fSVladimir Kondratiev 	/* register write */
3042be7d22fSVladimir Kondratiev #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
3052be7d22fSVladimir Kondratiev 	/* register set = read, OR, write */
306972072aaSVladimir Kondratiev #define S(a, v) W(a, R(a) | v)
307972072aaSVladimir Kondratiev 	/* register clear = read, AND with inverted, write */
308972072aaSVladimir Kondratiev #define C(a, v) W(a, R(a) & ~v)
3092be7d22fSVladimir Kondratiev 
31017123991SVladimir Kondratiev 	wil->hw_version = R(RGF_USER_FW_REV_ID);
31136b10a72SVladimir Kondratiev 	rev_id = wil->hw_version & 0xff;
3122be7d22fSVladimir Kondratiev 	/* hpal_perst_from_pad_src_n_mask */
3132be7d22fSVladimir Kondratiev 	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
3142be7d22fSVladimir Kondratiev 	/* car_perst_rst_src_n_mask */
3152be7d22fSVladimir Kondratiev 	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
316260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
3172be7d22fSVladimir Kondratiev 
3182be7d22fSVladimir Kondratiev 	W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */
3192be7d22fSVladimir Kondratiev 	W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
320260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
3212be7d22fSVladimir Kondratiev 
3222be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
3232be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
3242be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
3252be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
326260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
3272be7d22fSVladimir Kondratiev 
3282be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
3292be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
3302be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
3312be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
332260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
3332be7d22fSVladimir Kondratiev 
3342be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
33536b10a72SVladimir Kondratiev 	if (rev_id == 1) {
3362be7d22fSVladimir Kondratiev 		W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
33736b10a72SVladimir Kondratiev 	} else {
33817123991SVladimir Kondratiev 		W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
33936b10a72SVladimir Kondratiev 		W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
34036b10a72SVladimir Kondratiev 	}
3412be7d22fSVladimir Kondratiev 	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
342260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
3432be7d22fSVladimir Kondratiev 
344d28bcc30SVladimir Kondratiev 	/* wait until device ready */
34536b10a72SVladimir Kondratiev 	do {
34636b10a72SVladimir Kondratiev 		msleep(1);
347d28bcc30SVladimir Kondratiev 		hw_state = R(RGF_USER_HW_MACHINE_STATE);
34898a65b59SVladimir Kondratiev 		if (delay++ > 100) {
349d28bcc30SVladimir Kondratiev 			wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
350d28bcc30SVladimir Kondratiev 				hw_state);
35136b10a72SVladimir Kondratiev 			return;
35236b10a72SVladimir Kondratiev 		}
353d28bcc30SVladimir Kondratiev 	} while (hw_state != HW_MACHINE_BOOT_DONE);
35436b10a72SVladimir Kondratiev 
35536b10a72SVladimir Kondratiev 	if (rev_id == 2)
35617123991SVladimir Kondratiev 		W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
35736b10a72SVladimir Kondratiev 
358972072aaSVladimir Kondratiev 	C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
359260e6951SVladimir Kondratiev 	wmb(); /* order is important here */
360972072aaSVladimir Kondratiev 
36198a65b59SVladimir Kondratiev 	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
3622be7d22fSVladimir Kondratiev 
36336b10a72SVladimir Kondratiev #undef R
3642be7d22fSVladimir Kondratiev #undef W
3652be7d22fSVladimir Kondratiev #undef S
366972072aaSVladimir Kondratiev #undef C
3672be7d22fSVladimir Kondratiev }
3682be7d22fSVladimir Kondratiev 
3692be7d22fSVladimir Kondratiev void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
3702be7d22fSVladimir Kondratiev {
3712be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->base);
3722be7d22fSVladimir Kondratiev 	le16_to_cpus(&r->entry_size);
3732be7d22fSVladimir Kondratiev 	le16_to_cpus(&r->size);
3742be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->tail);
3752be7d22fSVladimir Kondratiev 	le32_to_cpus(&r->head);
3762be7d22fSVladimir Kondratiev }
3772be7d22fSVladimir Kondratiev 
3782be7d22fSVladimir Kondratiev static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
3792be7d22fSVladimir Kondratiev {
3802be7d22fSVladimir Kondratiev 	ulong to = msecs_to_jiffies(1000);
3812be7d22fSVladimir Kondratiev 	ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
3822be7d22fSVladimir Kondratiev 	if (0 == left) {
3832be7d22fSVladimir Kondratiev 		wil_err(wil, "Firmware not ready\n");
3842be7d22fSVladimir Kondratiev 		return -ETIME;
3852be7d22fSVladimir Kondratiev 	} else {
38615e23124SVladimir Kondratiev 		wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
38715e23124SVladimir Kondratiev 			 jiffies_to_msecs(to-left), wil->hw_version);
3882be7d22fSVladimir Kondratiev 	}
3892be7d22fSVladimir Kondratiev 	return 0;
3902be7d22fSVladimir Kondratiev }
3912be7d22fSVladimir Kondratiev 
3922be7d22fSVladimir Kondratiev /*
3932be7d22fSVladimir Kondratiev  * We reset all the structures, and we reset the UMAC.
3942be7d22fSVladimir Kondratiev  * After calling this routine, you're expected to reload
3952be7d22fSVladimir Kondratiev  * the firmware.
3962be7d22fSVladimir Kondratiev  */
3972be7d22fSVladimir Kondratiev int wil_reset(struct wil6210_priv *wil)
3982be7d22fSVladimir Kondratiev {
3992be7d22fSVladimir Kondratiev 	int rc;
4002be7d22fSVladimir Kondratiev 
401097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
402097638a0SVladimir Kondratiev 
403097638a0SVladimir Kondratiev 	cancel_work_sync(&wil->disconnect_worker);
404097638a0SVladimir Kondratiev 	wil6210_disconnect(wil, NULL);
405097638a0SVladimir Kondratiev 
4060fef1818SVladimir Kondratiev 	wil->status = 0; /* prevent NAPI from being scheduled */
4070fef1818SVladimir Kondratiev 	if (test_bit(wil_status_napi_en, &wil->status)) {
4080fef1818SVladimir Kondratiev 		napi_synchronize(&wil->napi_rx);
4090fef1818SVladimir Kondratiev 	}
4100fef1818SVladimir Kondratiev 
411ed6f9dc6SVladimir Kondratiev 	if (wil->scan_request) {
412ed6f9dc6SVladimir Kondratiev 		wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
413ed6f9dc6SVladimir Kondratiev 			     wil->scan_request);
414ed6f9dc6SVladimir Kondratiev 		cfg80211_scan_done(wil->scan_request, true);
415ed6f9dc6SVladimir Kondratiev 		wil->scan_request = NULL;
416ed6f9dc6SVladimir Kondratiev 	}
417ed6f9dc6SVladimir Kondratiev 
4182be7d22fSVladimir Kondratiev 	wil6210_disable_irq(wil);
4192be7d22fSVladimir Kondratiev 
420e08b5906SVladimir Kondratiev 	wmi_event_flush(wil);
421e08b5906SVladimir Kondratiev 
422e08b5906SVladimir Kondratiev 	flush_workqueue(wil->wmi_wq_conn);
423e08b5906SVladimir Kondratiev 	flush_workqueue(wil->wmi_wq);
424e08b5906SVladimir Kondratiev 
4252be7d22fSVladimir Kondratiev 	/* TODO: put MAC in reset */
4262be7d22fSVladimir Kondratiev 	wil_target_reset(wil);
4272be7d22fSVladimir Kondratiev 
4288bf6adb9SVladimir Kondratiev 	wil_rx_fini(wil);
4298bf6adb9SVladimir Kondratiev 
4302be7d22fSVladimir Kondratiev 	/* init after reset */
4312be7d22fSVladimir Kondratiev 	wil->pending_connect_cid = -1;
43216735d02SWolfram Sang 	reinit_completion(&wil->wmi_ready);
4332be7d22fSVladimir Kondratiev 
4342be7d22fSVladimir Kondratiev 	/* TODO: release MAC reset */
4352be7d22fSVladimir Kondratiev 	wil6210_enable_irq(wil);
4362be7d22fSVladimir Kondratiev 
4372be7d22fSVladimir Kondratiev 	/* we just started MAC, wait for FW ready */
4382be7d22fSVladimir Kondratiev 	rc = wil_wait_for_fw_ready(wil);
4392be7d22fSVladimir Kondratiev 
4402be7d22fSVladimir Kondratiev 	return rc;
4412be7d22fSVladimir Kondratiev }
4422be7d22fSVladimir Kondratiev 
443ed6f9dc6SVladimir Kondratiev void wil_fw_error_recovery(struct wil6210_priv *wil)
444ed6f9dc6SVladimir Kondratiev {
445ed6f9dc6SVladimir Kondratiev 	wil_dbg_misc(wil, "starting fw error recovery\n");
446ed6f9dc6SVladimir Kondratiev 	schedule_work(&wil->fw_error_worker);
447ed6f9dc6SVladimir Kondratiev }
4482be7d22fSVladimir Kondratiev 
4492be7d22fSVladimir Kondratiev void wil_link_on(struct wil6210_priv *wil)
4502be7d22fSVladimir Kondratiev {
4512be7d22fSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
4522be7d22fSVladimir Kondratiev 
4537743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "%s()\n", __func__);
4542be7d22fSVladimir Kondratiev 
4552be7d22fSVladimir Kondratiev 	netif_carrier_on(ndev);
4562be7d22fSVladimir Kondratiev 	netif_tx_wake_all_queues(ndev);
4572be7d22fSVladimir Kondratiev }
4582be7d22fSVladimir Kondratiev 
4592be7d22fSVladimir Kondratiev void wil_link_off(struct wil6210_priv *wil)
4602be7d22fSVladimir Kondratiev {
4612be7d22fSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
4622be7d22fSVladimir Kondratiev 
4637743882dSVladimir Kondratiev 	wil_dbg_misc(wil, "%s()\n", __func__);
4642be7d22fSVladimir Kondratiev 
4652be7d22fSVladimir Kondratiev 	netif_tx_stop_all_queues(ndev);
4662be7d22fSVladimir Kondratiev 	netif_carrier_off(ndev);
4672be7d22fSVladimir Kondratiev }
4682be7d22fSVladimir Kondratiev 
4692be7d22fSVladimir Kondratiev static int __wil_up(struct wil6210_priv *wil)
4702be7d22fSVladimir Kondratiev {
4712be7d22fSVladimir Kondratiev 	struct net_device *ndev = wil_to_ndev(wil);
4722be7d22fSVladimir Kondratiev 	struct wireless_dev *wdev = wil->wdev;
4732be7d22fSVladimir Kondratiev 	int rc;
4742be7d22fSVladimir Kondratiev 
475097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
476097638a0SVladimir Kondratiev 
4772be7d22fSVladimir Kondratiev 	rc = wil_reset(wil);
4782be7d22fSVladimir Kondratiev 	if (rc)
4792be7d22fSVladimir Kondratiev 		return rc;
4802be7d22fSVladimir Kondratiev 
481e31b2562SVladimir Kondratiev 	/* Rx VRING. After MAC and beacon */
482e31b2562SVladimir Kondratiev 	rc = wil_rx_init(wil);
483e31b2562SVladimir Kondratiev 	if (rc)
484e31b2562SVladimir Kondratiev 		return rc;
485e31b2562SVladimir Kondratiev 
4862be7d22fSVladimir Kondratiev 	switch (wdev->iftype) {
4872be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_STATION:
4887743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: STATION\n");
4892be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
4902be7d22fSVladimir Kondratiev 		break;
4912be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_AP:
4927743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: AP\n");
4932be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
4942be7d22fSVladimir Kondratiev 		break;
4952be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_CLIENT:
4967743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: P2P_CLIENT\n");
4972be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
4982be7d22fSVladimir Kondratiev 		break;
4992be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_P2P_GO:
5007743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: P2P_GO\n");
5012be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_ETHER;
5022be7d22fSVladimir Kondratiev 		break;
5032be7d22fSVladimir Kondratiev 	case NL80211_IFTYPE_MONITOR:
5047743882dSVladimir Kondratiev 		wil_dbg_misc(wil, "type: Monitor\n");
5052be7d22fSVladimir Kondratiev 		ndev->type = ARPHRD_IEEE80211_RADIOTAP;
5062be7d22fSVladimir Kondratiev 		/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
5072be7d22fSVladimir Kondratiev 		break;
5082be7d22fSVladimir Kondratiev 	default:
5092be7d22fSVladimir Kondratiev 		return -EOPNOTSUPP;
5102be7d22fSVladimir Kondratiev 	}
5112be7d22fSVladimir Kondratiev 
5122be7d22fSVladimir Kondratiev 	/* MAC address - pre-requisite for other commands */
5132be7d22fSVladimir Kondratiev 	wmi_set_mac_address(wil, ndev->dev_addr);
5142be7d22fSVladimir Kondratiev 
5152be7d22fSVladimir Kondratiev 
516e0287c4aSVladimir Kondratiev 	napi_enable(&wil->napi_rx);
517e0287c4aSVladimir Kondratiev 	napi_enable(&wil->napi_tx);
5180fef1818SVladimir Kondratiev 	set_bit(wil_status_napi_en, &wil->status);
519e0287c4aSVladimir Kondratiev 
5202be7d22fSVladimir Kondratiev 	return 0;
5212be7d22fSVladimir Kondratiev }
5222be7d22fSVladimir Kondratiev 
5232be7d22fSVladimir Kondratiev int wil_up(struct wil6210_priv *wil)
5242be7d22fSVladimir Kondratiev {
5252be7d22fSVladimir Kondratiev 	int rc;
5262be7d22fSVladimir Kondratiev 
5272be7d22fSVladimir Kondratiev 	mutex_lock(&wil->mutex);
5282be7d22fSVladimir Kondratiev 	rc = __wil_up(wil);
5292be7d22fSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
5302be7d22fSVladimir Kondratiev 
5312be7d22fSVladimir Kondratiev 	return rc;
5322be7d22fSVladimir Kondratiev }
5332be7d22fSVladimir Kondratiev 
5342be7d22fSVladimir Kondratiev static int __wil_down(struct wil6210_priv *wil)
5352be7d22fSVladimir Kondratiev {
536097638a0SVladimir Kondratiev 	WARN_ON(!mutex_is_locked(&wil->mutex));
537097638a0SVladimir Kondratiev 
5380fef1818SVladimir Kondratiev 	clear_bit(wil_status_napi_en, &wil->status);
539e0287c4aSVladimir Kondratiev 	napi_disable(&wil->napi_rx);
540e0287c4aSVladimir Kondratiev 	napi_disable(&wil->napi_tx);
541e0287c4aSVladimir Kondratiev 
5422be7d22fSVladimir Kondratiev 	if (wil->scan_request) {
5432be7d22fSVladimir Kondratiev 		cfg80211_scan_done(wil->scan_request, true);
5442be7d22fSVladimir Kondratiev 		wil->scan_request = NULL;
5452be7d22fSVladimir Kondratiev 	}
5462be7d22fSVladimir Kondratiev 
5472be7d22fSVladimir Kondratiev 	wil6210_disconnect(wil, NULL);
5482be7d22fSVladimir Kondratiev 	wil_rx_fini(wil);
5492be7d22fSVladimir Kondratiev 
5502be7d22fSVladimir Kondratiev 	return 0;
5512be7d22fSVladimir Kondratiev }
5522be7d22fSVladimir Kondratiev 
5532be7d22fSVladimir Kondratiev int wil_down(struct wil6210_priv *wil)
5542be7d22fSVladimir Kondratiev {
5552be7d22fSVladimir Kondratiev 	int rc;
5562be7d22fSVladimir Kondratiev 
5572be7d22fSVladimir Kondratiev 	mutex_lock(&wil->mutex);
5582be7d22fSVladimir Kondratiev 	rc = __wil_down(wil);
5592be7d22fSVladimir Kondratiev 	mutex_unlock(&wil->mutex);
5602be7d22fSVladimir Kondratiev 
5612be7d22fSVladimir Kondratiev 	return rc;
5622be7d22fSVladimir Kondratiev }
5633df2cd36SVladimir Kondratiev 
5643df2cd36SVladimir Kondratiev int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
5653df2cd36SVladimir Kondratiev {
5663df2cd36SVladimir Kondratiev 	int i;
5673df2cd36SVladimir Kondratiev 	int rc = -ENOENT;
5683df2cd36SVladimir Kondratiev 
5693df2cd36SVladimir Kondratiev 	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
5703df2cd36SVladimir Kondratiev 		if ((wil->sta[i].status != wil_sta_unused) &&
571108d1eb6SVladimir Kondratiev 		    ether_addr_equal(wil->sta[i].addr, mac)) {
5723df2cd36SVladimir Kondratiev 			rc = i;
5733df2cd36SVladimir Kondratiev 			break;
5743df2cd36SVladimir Kondratiev 		}
5753df2cd36SVladimir Kondratiev 	}
5763df2cd36SVladimir Kondratiev 
5773df2cd36SVladimir Kondratiev 	return rc;
5783df2cd36SVladimir Kondratiev }
579