12be7d22fSVladimir Kondratiev /* 202525a79SVladimir Kondratiev * Copyright (c) 2012-2014 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" 24f172b563SDedy Lansky 25f172b563SDedy Lansky #define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000 26f172b563SDedy Lansky #define WAIT_FOR_DISCONNECT_INTERVAL_MS 10 272be7d22fSVladimir Kondratiev 28c33407a8SVladimir Kondratiev bool no_fw_recovery; 29ed6f9dc6SVladimir Kondratiev module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); 30c33407a8SVladimir Kondratiev MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery"); 31ed6f9dc6SVladimir Kondratiev 32151a9706SVladimir Kondratiev static bool no_fw_load = true; 33151a9706SVladimir Kondratiev module_param(no_fw_load, bool, S_IRUGO | S_IWUSR); 34151a9706SVladimir Kondratiev MODULE_PARM_DESC(no_fw_load, " do not download FW, use one in on-card flash."); 35151a9706SVladimir Kondratiev 36b6b1b0ecSVladimir Kondratiev static unsigned int itr_trsh = WIL6210_ITR_TRSH_DEFAULT; 37b6b1b0ecSVladimir Kondratiev 38b6b1b0ecSVladimir Kondratiev module_param(itr_trsh, uint, S_IRUGO); 39b6b1b0ecSVladimir Kondratiev MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs."); 40b6b1b0ecSVladimir Kondratiev 41520d68e7SVladimir Kondratiev #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */ 42520d68e7SVladimir Kondratiev #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */ 43520d68e7SVladimir Kondratiev 442be7d22fSVladimir Kondratiev /* 452be7d22fSVladimir Kondratiev * Due to a hardware issue, 462be7d22fSVladimir Kondratiev * one has to read/write to/from NIC in 32-bit chunks; 472be7d22fSVladimir Kondratiev * regular memcpy_fromio and siblings will 482be7d22fSVladimir Kondratiev * not work on 64-bit platform - it uses 64-bit transactions 492be7d22fSVladimir Kondratiev * 502be7d22fSVladimir Kondratiev * Force 32-bit transactions to enable NIC on 64-bit platforms 512be7d22fSVladimir Kondratiev * 522be7d22fSVladimir Kondratiev * To avoid byte swap on big endian host, __raw_{read|write}l 532be7d22fSVladimir Kondratiev * should be used - {read|write}l would swap bytes to provide 542be7d22fSVladimir Kondratiev * little endian on PCI value in host endianness. 552be7d22fSVladimir Kondratiev */ 562be7d22fSVladimir Kondratiev void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, 572be7d22fSVladimir Kondratiev size_t count) 582be7d22fSVladimir Kondratiev { 592be7d22fSVladimir Kondratiev u32 *d = dst; 602be7d22fSVladimir Kondratiev const volatile u32 __iomem *s = src; 612be7d22fSVladimir Kondratiev 622be7d22fSVladimir Kondratiev /* size_t is unsigned, if (count%4 != 0) it will wrap */ 632be7d22fSVladimir Kondratiev for (count += 4; count > 4; count -= 4) 642be7d22fSVladimir Kondratiev *d++ = __raw_readl(s++); 652be7d22fSVladimir Kondratiev } 662be7d22fSVladimir Kondratiev 672be7d22fSVladimir Kondratiev void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, 682be7d22fSVladimir Kondratiev size_t count) 692be7d22fSVladimir Kondratiev { 702be7d22fSVladimir Kondratiev volatile u32 __iomem *d = dst; 712be7d22fSVladimir Kondratiev const u32 *s = src; 722be7d22fSVladimir Kondratiev 732be7d22fSVladimir Kondratiev for (count += 4; count > 4; count -= 4) 742be7d22fSVladimir Kondratiev __raw_writel(*s++, d++); 752be7d22fSVladimir Kondratiev } 762be7d22fSVladimir Kondratiev 7791886b0bSVladimir Kondratiev static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) 782be7d22fSVladimir Kondratiev { 7991886b0bSVladimir Kondratiev uint i; 80fc58f681SVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 81fc58f681SVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 82b4490f42SVladimir Kondratiev struct wil_sta_info *sta = &wil->sta[cid]; 838fe59627SVladimir Kondratiev 84fc58f681SVladimir Kondratiev wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, 85fc58f681SVladimir Kondratiev sta->status); 864d55a0a1SVladimir Kondratiev 87e58c9f70SVladimir Kondratiev sta->data_port_open = false; 884d55a0a1SVladimir Kondratiev if (sta->status != wil_sta_unused) { 894d55a0a1SVladimir Kondratiev wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); 90fc58f681SVladimir Kondratiev switch (wdev->iftype) { 91fc58f681SVladimir Kondratiev case NL80211_IFTYPE_AP: 92fc58f681SVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 93fc58f681SVladimir Kondratiev /* AP-like interface */ 94fc58f681SVladimir Kondratiev cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); 95fc58f681SVladimir Kondratiev break; 96fc58f681SVladimir Kondratiev default: 97fc58f681SVladimir Kondratiev break; 98fc58f681SVladimir Kondratiev } 994d55a0a1SVladimir Kondratiev sta->status = wil_sta_unused; 1004d55a0a1SVladimir Kondratiev } 1014d55a0a1SVladimir Kondratiev 102b4490f42SVladimir Kondratiev for (i = 0; i < WIL_STA_TID_NUM; i++) { 103ec81b5adSDedy Lansky struct wil_tid_ampdu_rx *r; 104ec81b5adSDedy Lansky unsigned long flags; 105ec81b5adSDedy Lansky 106ec81b5adSDedy Lansky spin_lock_irqsave(&sta->tid_rx_lock, flags); 107ec81b5adSDedy Lansky 108ec81b5adSDedy Lansky r = sta->tid_rx[i]; 109b4490f42SVladimir Kondratiev sta->tid_rx[i] = NULL; 110b4490f42SVladimir Kondratiev wil_tid_ampdu_rx_free(wil, r); 111ec81b5adSDedy Lansky 112ec81b5adSDedy Lansky spin_unlock_irqrestore(&sta->tid_rx_lock, flags); 113b4490f42SVladimir Kondratiev } 11491886b0bSVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { 11591886b0bSVladimir Kondratiev if (wil->vring2cid_tid[i][0] == cid) 11691886b0bSVladimir Kondratiev wil_vring_fini_tx(wil, i); 11791886b0bSVladimir Kondratiev } 11891886b0bSVladimir Kondratiev memset(&sta->stats, 0, sizeof(sta->stats)); 119b4490f42SVladimir Kondratiev } 120b4490f42SVladimir Kondratiev 1213b3a0162SJohannes Berg static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) 12291886b0bSVladimir Kondratiev { 12391886b0bSVladimir Kondratiev int cid = -ENOENT; 12491886b0bSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 12591886b0bSVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 12691886b0bSVladimir Kondratiev 12791886b0bSVladimir Kondratiev might_sleep(); 12891886b0bSVladimir Kondratiev if (bssid) { 12991886b0bSVladimir Kondratiev cid = wil_find_cid(wil, bssid); 13091886b0bSVladimir Kondratiev wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); 13191886b0bSVladimir Kondratiev } else { 13291886b0bSVladimir Kondratiev wil_dbg_misc(wil, "%s(all)\n", __func__); 13391886b0bSVladimir Kondratiev } 13491886b0bSVladimir Kondratiev 13591886b0bSVladimir Kondratiev if (cid >= 0) /* disconnect 1 peer */ 13691886b0bSVladimir Kondratiev wil_disconnect_cid(wil, cid); 13791886b0bSVladimir Kondratiev else /* disconnect all */ 13891886b0bSVladimir Kondratiev for (cid = 0; cid < WIL6210_MAX_CID; cid++) 13991886b0bSVladimir Kondratiev wil_disconnect_cid(wil, cid); 14091886b0bSVladimir Kondratiev 14191886b0bSVladimir Kondratiev /* link state */ 14291886b0bSVladimir Kondratiev switch (wdev->iftype) { 14391886b0bSVladimir Kondratiev case NL80211_IFTYPE_STATION: 14491886b0bSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 1452be7d22fSVladimir Kondratiev wil_link_off(wil); 146b338f74eSVladimir Kondratiev if (test_bit(wil_status_fwconnected, &wil->status)) { 1472be7d22fSVladimir Kondratiev clear_bit(wil_status_fwconnected, &wil->status); 148b338f74eSVladimir Kondratiev cfg80211_disconnected(ndev, 149b338f74eSVladimir Kondratiev WLAN_STATUS_UNSPECIFIED_FAILURE, 1502be7d22fSVladimir Kondratiev NULL, 0, GFP_KERNEL); 151b338f74eSVladimir Kondratiev } else if (test_bit(wil_status_fwconnecting, &wil->status)) { 1522be7d22fSVladimir Kondratiev cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, 1532be7d22fSVladimir Kondratiev WLAN_STATUS_UNSPECIFIED_FAILURE, 1542be7d22fSVladimir Kondratiev GFP_KERNEL); 1552be7d22fSVladimir Kondratiev } 156b338f74eSVladimir Kondratiev clear_bit(wil_status_fwconnecting, &wil->status); 15791886b0bSVladimir Kondratiev break; 15891886b0bSVladimir Kondratiev default: 15991886b0bSVladimir Kondratiev break; 16091886b0bSVladimir Kondratiev } 1612be7d22fSVladimir Kondratiev } 1622be7d22fSVladimir Kondratiev 1632be7d22fSVladimir Kondratiev static void wil_disconnect_worker(struct work_struct *work) 1642be7d22fSVladimir Kondratiev { 1652be7d22fSVladimir Kondratiev struct wil6210_priv *wil = container_of(work, 1662be7d22fSVladimir Kondratiev struct wil6210_priv, disconnect_worker); 1672be7d22fSVladimir Kondratiev 168097638a0SVladimir Kondratiev mutex_lock(&wil->mutex); 1692be7d22fSVladimir Kondratiev _wil6210_disconnect(wil, NULL); 170097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex); 1712be7d22fSVladimir Kondratiev } 1722be7d22fSVladimir Kondratiev 1732be7d22fSVladimir Kondratiev static void wil_connect_timer_fn(ulong x) 1742be7d22fSVladimir Kondratiev { 1752be7d22fSVladimir Kondratiev struct wil6210_priv *wil = (void *)x; 1762be7d22fSVladimir Kondratiev 1777743882dSVladimir Kondratiev wil_dbg_misc(wil, "Connect timeout\n"); 1782be7d22fSVladimir Kondratiev 1792be7d22fSVladimir Kondratiev /* reschedule to thread context - disconnect won't 1802be7d22fSVladimir Kondratiev * run from atomic context 1812be7d22fSVladimir Kondratiev */ 1822be7d22fSVladimir Kondratiev schedule_work(&wil->disconnect_worker); 1832be7d22fSVladimir Kondratiev } 1842be7d22fSVladimir Kondratiev 185047e5d74SVladimir Kondratiev static void wil_scan_timer_fn(ulong x) 186047e5d74SVladimir Kondratiev { 187047e5d74SVladimir Kondratiev struct wil6210_priv *wil = (void *)x; 188047e5d74SVladimir Kondratiev 189047e5d74SVladimir Kondratiev clear_bit(wil_status_fwready, &wil->status); 190047e5d74SVladimir Kondratiev wil_err(wil, "Scan timeout detected, start fw error recovery\n"); 191047e5d74SVladimir Kondratiev schedule_work(&wil->fw_error_worker); 192047e5d74SVladimir Kondratiev } 193047e5d74SVladimir Kondratiev 194c33407a8SVladimir Kondratiev static int wil_wait_for_recovery(struct wil6210_priv *wil) 195c33407a8SVladimir Kondratiev { 196c33407a8SVladimir Kondratiev if (wait_event_interruptible(wil->wq, wil->recovery_state != 197c33407a8SVladimir Kondratiev fw_recovery_pending)) { 198c33407a8SVladimir Kondratiev wil_err(wil, "Interrupt, canceling recovery\n"); 199c33407a8SVladimir Kondratiev return -ERESTARTSYS; 200c33407a8SVladimir Kondratiev } 201c33407a8SVladimir Kondratiev if (wil->recovery_state != fw_recovery_running) { 202c33407a8SVladimir Kondratiev wil_info(wil, "Recovery cancelled\n"); 203c33407a8SVladimir Kondratiev return -EINTR; 204c33407a8SVladimir Kondratiev } 205c33407a8SVladimir Kondratiev wil_info(wil, "Proceed with recovery\n"); 206c33407a8SVladimir Kondratiev return 0; 207c33407a8SVladimir Kondratiev } 208c33407a8SVladimir Kondratiev 209c33407a8SVladimir Kondratiev void wil_set_recovery_state(struct wil6210_priv *wil, int state) 210c33407a8SVladimir Kondratiev { 211c33407a8SVladimir Kondratiev wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__, 212c33407a8SVladimir Kondratiev wil->recovery_state, state); 213c33407a8SVladimir Kondratiev 214c33407a8SVladimir Kondratiev wil->recovery_state = state; 215c33407a8SVladimir Kondratiev wake_up_interruptible(&wil->wq); 216c33407a8SVladimir Kondratiev } 217c33407a8SVladimir Kondratiev 218ed6f9dc6SVladimir Kondratiev static void wil_fw_error_worker(struct work_struct *work) 219ed6f9dc6SVladimir Kondratiev { 220c33407a8SVladimir Kondratiev struct wil6210_priv *wil = container_of(work, struct wil6210_priv, 221c33407a8SVladimir Kondratiev fw_error_worker); 222ed6f9dc6SVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 223ed6f9dc6SVladimir Kondratiev 224ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "fw error worker\n"); 225ed6f9dc6SVladimir Kondratiev 226fc219eedSVladimir Kondratiev /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO 227fc219eedSVladimir Kondratiev * passed since last recovery attempt 228fc219eedSVladimir Kondratiev */ 229fc219eedSVladimir Kondratiev if (time_is_after_jiffies(wil->last_fw_recovery + 230fc219eedSVladimir Kondratiev WIL6210_FW_RECOVERY_TO)) 231fc219eedSVladimir Kondratiev wil->recovery_count++; 232fc219eedSVladimir Kondratiev else 233fc219eedSVladimir Kondratiev wil->recovery_count = 1; /* fw was alive for a long time */ 234fc219eedSVladimir Kondratiev 235fc219eedSVladimir Kondratiev if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { 236fc219eedSVladimir Kondratiev wil_err(wil, "too many recovery attempts (%d), giving up\n", 237fc219eedSVladimir Kondratiev wil->recovery_count); 238fc219eedSVladimir Kondratiev return; 239fc219eedSVladimir Kondratiev } 240fc219eedSVladimir Kondratiev 241fc219eedSVladimir Kondratiev wil->last_fw_recovery = jiffies; 242fc219eedSVladimir Kondratiev 2439c3bde56SVladimir Kondratiev mutex_lock(&wil->mutex); 244ed6f9dc6SVladimir Kondratiev switch (wdev->iftype) { 245ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_STATION: 246ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 247ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_MONITOR: 248c33407a8SVladimir Kondratiev wil_info(wil, "fw error recovery requested (try %d)...\n", 249fc219eedSVladimir Kondratiev wil->recovery_count); 250c33407a8SVladimir Kondratiev if (!no_fw_recovery) 251c33407a8SVladimir Kondratiev wil->recovery_state = fw_recovery_running; 252c33407a8SVladimir Kondratiev if (0 != wil_wait_for_recovery(wil)) 253c33407a8SVladimir Kondratiev break; 254c33407a8SVladimir Kondratiev 25573d839aeSVladimir Kondratiev __wil_down(wil); 25673d839aeSVladimir Kondratiev __wil_up(wil); 257ed6f9dc6SVladimir Kondratiev break; 258ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_AP: 259ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 260ed6f9dc6SVladimir Kondratiev /* recovery in these modes is done by upper layers */ 261ed6f9dc6SVladimir Kondratiev break; 262ed6f9dc6SVladimir Kondratiev default: 263ed6f9dc6SVladimir Kondratiev break; 264ed6f9dc6SVladimir Kondratiev } 2659c3bde56SVladimir Kondratiev mutex_unlock(&wil->mutex); 266ed6f9dc6SVladimir Kondratiev } 267ed6f9dc6SVladimir Kondratiev 2689a177384SVladimir Kondratiev static int wil_find_free_vring(struct wil6210_priv *wil) 2699a177384SVladimir Kondratiev { 2709a177384SVladimir Kondratiev int i; 2718fe59627SVladimir Kondratiev 2729a177384SVladimir Kondratiev for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { 2739a177384SVladimir Kondratiev if (!wil->vring_tx[i].va) 2749a177384SVladimir Kondratiev return i; 2759a177384SVladimir Kondratiev } 2769a177384SVladimir Kondratiev return -EINVAL; 2779a177384SVladimir Kondratiev } 2789a177384SVladimir Kondratiev 279d81079f1SVladimir Kondratiev static void wil_connect_worker(struct work_struct *work) 280d81079f1SVladimir Kondratiev { 281d81079f1SVladimir Kondratiev int rc; 282d81079f1SVladimir Kondratiev struct wil6210_priv *wil = container_of(work, struct wil6210_priv, 283d81079f1SVladimir Kondratiev connect_worker); 284d81079f1SVladimir Kondratiev int cid = wil->pending_connect_cid; 2859a177384SVladimir Kondratiev int ringid = wil_find_free_vring(wil); 286d81079f1SVladimir Kondratiev 287d81079f1SVladimir Kondratiev if (cid < 0) { 288d81079f1SVladimir Kondratiev wil_err(wil, "No connection pending\n"); 289d81079f1SVladimir Kondratiev return; 290d81079f1SVladimir Kondratiev } 291d81079f1SVladimir Kondratiev 292d81079f1SVladimir Kondratiev wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); 293d81079f1SVladimir Kondratiev 2949a177384SVladimir Kondratiev rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); 295d81079f1SVladimir Kondratiev wil->pending_connect_cid = -1; 2963df2cd36SVladimir Kondratiev if (rc == 0) { 2973df2cd36SVladimir Kondratiev wil->sta[cid].status = wil_sta_connected; 298d81079f1SVladimir Kondratiev wil_link_on(wil); 2993df2cd36SVladimir Kondratiev } else { 3003df2cd36SVladimir Kondratiev wil->sta[cid].status = wil_sta_unused; 3013df2cd36SVladimir Kondratiev } 302d81079f1SVladimir Kondratiev } 303d81079f1SVladimir Kondratiev 3042be7d22fSVladimir Kondratiev int wil_priv_init(struct wil6210_priv *wil) 3052be7d22fSVladimir Kondratiev { 306ec81b5adSDedy Lansky uint i; 307ec81b5adSDedy Lansky 3087743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 3092be7d22fSVladimir Kondratiev 3103df2cd36SVladimir Kondratiev memset(wil->sta, 0, sizeof(wil->sta)); 311ec81b5adSDedy Lansky for (i = 0; i < WIL6210_MAX_CID; i++) 312ec81b5adSDedy Lansky spin_lock_init(&wil->sta[i].tid_rx_lock); 3133df2cd36SVladimir Kondratiev 3142be7d22fSVladimir Kondratiev mutex_init(&wil->mutex); 3152be7d22fSVladimir Kondratiev mutex_init(&wil->wmi_mutex); 3162be7d22fSVladimir Kondratiev 3172be7d22fSVladimir Kondratiev init_completion(&wil->wmi_ready); 31859502647SDedy Lansky init_completion(&wil->wmi_call); 3192be7d22fSVladimir Kondratiev 3202be7d22fSVladimir Kondratiev wil->pending_connect_cid = -1; 3212be7d22fSVladimir Kondratiev setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); 322047e5d74SVladimir Kondratiev setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); 3232be7d22fSVladimir Kondratiev 324d81079f1SVladimir Kondratiev INIT_WORK(&wil->connect_worker, wil_connect_worker); 3252be7d22fSVladimir Kondratiev INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); 3262be7d22fSVladimir Kondratiev INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); 327ed6f9dc6SVladimir Kondratiev INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); 3282be7d22fSVladimir Kondratiev 3292be7d22fSVladimir Kondratiev INIT_LIST_HEAD(&wil->pending_wmi_ev); 3302be7d22fSVladimir Kondratiev spin_lock_init(&wil->wmi_ev_lock); 331c33407a8SVladimir Kondratiev init_waitqueue_head(&wil->wq); 3322be7d22fSVladimir Kondratiev 3332be7d22fSVladimir Kondratiev wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); 3342be7d22fSVladimir Kondratiev if (!wil->wmi_wq) 3352be7d22fSVladimir Kondratiev return -EAGAIN; 3362be7d22fSVladimir Kondratiev 3372be7d22fSVladimir Kondratiev wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); 3382be7d22fSVladimir Kondratiev if (!wil->wmi_wq_conn) { 3392be7d22fSVladimir Kondratiev destroy_workqueue(wil->wmi_wq); 3402be7d22fSVladimir Kondratiev return -EAGAIN; 3412be7d22fSVladimir Kondratiev } 3422be7d22fSVladimir Kondratiev 343fc219eedSVladimir Kondratiev wil->last_fw_recovery = jiffies; 344b6b1b0ecSVladimir Kondratiev wil->itr_trsh = itr_trsh; 345fc219eedSVladimir Kondratiev 3462be7d22fSVladimir Kondratiev return 0; 3472be7d22fSVladimir Kondratiev } 3482be7d22fSVladimir Kondratiev 3493b3a0162SJohannes Berg void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) 3502be7d22fSVladimir Kondratiev { 3519cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 3529cf10d62SVladimir Kondratiev 3532be7d22fSVladimir Kondratiev del_timer_sync(&wil->connect_timer); 3542be7d22fSVladimir Kondratiev _wil6210_disconnect(wil, bssid); 3552be7d22fSVladimir Kondratiev } 3562be7d22fSVladimir Kondratiev 3572be7d22fSVladimir Kondratiev void wil_priv_deinit(struct wil6210_priv *wil) 3582be7d22fSVladimir Kondratiev { 3599cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 3609cf10d62SVladimir Kondratiev 361c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 362047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 3632be7d22fSVladimir Kondratiev cancel_work_sync(&wil->disconnect_worker); 364ed6f9dc6SVladimir Kondratiev cancel_work_sync(&wil->fw_error_worker); 365097638a0SVladimir Kondratiev mutex_lock(&wil->mutex); 3662be7d22fSVladimir Kondratiev wil6210_disconnect(wil, NULL); 367097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex); 3682be7d22fSVladimir Kondratiev wmi_event_flush(wil); 3692be7d22fSVladimir Kondratiev destroy_workqueue(wil->wmi_wq_conn); 3702be7d22fSVladimir Kondratiev destroy_workqueue(wil->wmi_wq); 3712be7d22fSVladimir Kondratiev } 3722be7d22fSVladimir Kondratiev 373151a9706SVladimir Kondratiev /* target operations */ 374151a9706SVladimir Kondratiev /* register read */ 375151a9706SVladimir Kondratiev #define R(a) ioread32(wil->csr + HOSTADDR(a)) 376151a9706SVladimir Kondratiev /* register write. wmb() to make sure it is completed */ 377151a9706SVladimir Kondratiev #define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0) 378151a9706SVladimir Kondratiev /* register set = read, OR, write */ 379151a9706SVladimir Kondratiev #define S(a, v) W(a, R(a) | v) 380151a9706SVladimir Kondratiev /* register clear = read, AND with inverted, write */ 381151a9706SVladimir Kondratiev #define C(a, v) W(a, R(a) & ~v) 382151a9706SVladimir Kondratiev 383151a9706SVladimir Kondratiev static inline void wil_halt_cpu(struct wil6210_priv *wil) 384151a9706SVladimir Kondratiev { 385151a9706SVladimir Kondratiev W(RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); 386151a9706SVladimir Kondratiev W(RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST); 387151a9706SVladimir Kondratiev } 388151a9706SVladimir Kondratiev 389151a9706SVladimir Kondratiev static inline void wil_release_cpu(struct wil6210_priv *wil) 390151a9706SVladimir Kondratiev { 391151a9706SVladimir Kondratiev /* Start CPU */ 392151a9706SVladimir Kondratiev W(RGF_USER_USER_CPU_0, 1); 393151a9706SVladimir Kondratiev } 394151a9706SVladimir Kondratiev 395bbb2adc7SVladimir Kondratiev static int wil_target_reset(struct wil6210_priv *wil) 3962be7d22fSVladimir Kondratiev { 39798a65b59SVladimir Kondratiev int delay = 0; 398d28bcc30SVladimir Kondratiev u32 hw_state; 39936b10a72SVladimir Kondratiev u32 rev_id; 4006508281bSVladimir Kondratiev bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW); 40136b10a72SVladimir Kondratiev 4026508281bSVladimir Kondratiev wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name); 4032be7d22fSVladimir Kondratiev 40417123991SVladimir Kondratiev wil->hw_version = R(RGF_USER_FW_REV_ID); 40536b10a72SVladimir Kondratiev rev_id = wil->hw_version & 0xff; 4066508281bSVladimir Kondratiev 4076508281bSVladimir Kondratiev /* Clear MAC link up */ 4086508281bSVladimir Kondratiev S(RGF_HP_CTRL, BIT(15)); 409151a9706SVladimir Kondratiev S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD); 410151a9706SVladimir Kondratiev S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST); 411151a9706SVladimir Kondratiev 412151a9706SVladimir Kondratiev wil_halt_cpu(wil); 413151a9706SVladimir Kondratiev C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); /* 40 MHz */ 4142be7d22fSVladimir Kondratiev 4156508281bSVladimir Kondratiev if (is_sparrow) { 4166508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); 417151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf); 4186508281bSVladimir Kondratiev } 4196508281bSVladimir Kondratiev 4202be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); 4212be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); 422151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000f0 : 0x00000170); 423151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00); 4242be7d22fSVladimir Kondratiev 4256508281bSVladimir Kondratiev if (is_sparrow) { 4266508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); 427151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0); 4286508281bSVladimir Kondratiev } 4296508281bSVladimir Kondratiev 4302be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); 4312be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); 4322be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); 4332be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); 4342be7d22fSVladimir Kondratiev 4356508281bSVladimir Kondratiev if (is_sparrow) { 4366508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); 4376508281bSVladimir Kondratiev /* reset A2 PCIE AHB */ 4386508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); 4396508281bSVladimir Kondratiev } else { 4402be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); 44136b10a72SVladimir Kondratiev if (rev_id == 1) { 4426508281bSVladimir Kondratiev /* reset A1 BOTH PCIE AHB & PCIE RGF */ 4432be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); 44436b10a72SVladimir Kondratiev } else { 44517123991SVladimir Kondratiev W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); 44636b10a72SVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); 44736b10a72SVladimir Kondratiev } 4486508281bSVladimir Kondratiev } 4496508281bSVladimir Kondratiev 4506508281bSVladimir Kondratiev /* TODO: check order here!!! Erez code is different */ 4512be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); 4522be7d22fSVladimir Kondratiev 453520d68e7SVladimir Kondratiev /* wait until device ready. typical time is 200..250 msec */ 45436b10a72SVladimir Kondratiev do { 455520d68e7SVladimir Kondratiev msleep(RST_DELAY); 456d28bcc30SVladimir Kondratiev hw_state = R(RGF_USER_HW_MACHINE_STATE); 457520d68e7SVladimir Kondratiev if (delay++ > RST_COUNT) { 458d28bcc30SVladimir Kondratiev wil_err(wil, "Reset not completed, hw_state 0x%08x\n", 459d28bcc30SVladimir Kondratiev hw_state); 460bbb2adc7SVladimir Kondratiev return -ETIME; 46136b10a72SVladimir Kondratiev } 462d28bcc30SVladimir Kondratiev } while (hw_state != HW_MACHINE_BOOT_DONE); 46336b10a72SVladimir Kondratiev 4646508281bSVladimir Kondratiev /* TODO: Erez check rev_id != 1 */ 4656508281bSVladimir Kondratiev if (!is_sparrow && (rev_id != 1)) 46617123991SVladimir Kondratiev W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); 46736b10a72SVladimir Kondratiev 468972072aaSVladimir Kondratiev C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 469972072aaSVladimir Kondratiev 470520d68e7SVladimir Kondratiev wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); 471bbb2adc7SVladimir Kondratiev return 0; 472151a9706SVladimir Kondratiev } 4732be7d22fSVladimir Kondratiev 474b6b1b0ecSVladimir Kondratiev /** 475b6b1b0ecSVladimir Kondratiev * wil_set_itr_trsh: - apply interrupt coalescing params 476b6b1b0ecSVladimir Kondratiev */ 477b6b1b0ecSVladimir Kondratiev void wil_set_itr_trsh(struct wil6210_priv *wil) 478b6b1b0ecSVladimir Kondratiev { 479b6b1b0ecSVladimir Kondratiev /* disable, use usec resolution */ 480b6b1b0ecSVladimir Kondratiev W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EXT_TICK); 481b6b1b0ecSVladimir Kondratiev 482b6b1b0ecSVladimir Kondratiev /* disable interrupt moderation for monitor 483b6b1b0ecSVladimir Kondratiev * to get better timestamp precision 484b6b1b0ecSVladimir Kondratiev */ 485b6b1b0ecSVladimir Kondratiev if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) 486b6b1b0ecSVladimir Kondratiev return; 487b6b1b0ecSVladimir Kondratiev 488b6b1b0ecSVladimir Kondratiev wil_info(wil, "set ITR_TRSH = %d usec\n", wil->itr_trsh); 489b6b1b0ecSVladimir Kondratiev W(RGF_DMA_ITR_CNT_TRSH, wil->itr_trsh); 490b6b1b0ecSVladimir Kondratiev W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EN | 491b6b1b0ecSVladimir Kondratiev BIT_DMA_ITR_CNT_CRL_EXT_TICK); /* start it */ 492b6b1b0ecSVladimir Kondratiev } 493b6b1b0ecSVladimir Kondratiev 49436b10a72SVladimir Kondratiev #undef R 4952be7d22fSVladimir Kondratiev #undef W 4962be7d22fSVladimir Kondratiev #undef S 497972072aaSVladimir Kondratiev #undef C 4982be7d22fSVladimir Kondratiev 4992be7d22fSVladimir Kondratiev void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) 5002be7d22fSVladimir Kondratiev { 5012be7d22fSVladimir Kondratiev le32_to_cpus(&r->base); 5022be7d22fSVladimir Kondratiev le16_to_cpus(&r->entry_size); 5032be7d22fSVladimir Kondratiev le16_to_cpus(&r->size); 5042be7d22fSVladimir Kondratiev le32_to_cpus(&r->tail); 5052be7d22fSVladimir Kondratiev le32_to_cpus(&r->head); 5062be7d22fSVladimir Kondratiev } 5072be7d22fSVladimir Kondratiev 5082be7d22fSVladimir Kondratiev static int wil_wait_for_fw_ready(struct wil6210_priv *wil) 5092be7d22fSVladimir Kondratiev { 5102be7d22fSVladimir Kondratiev ulong to = msecs_to_jiffies(1000); 5112be7d22fSVladimir Kondratiev ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); 5128fe59627SVladimir Kondratiev 5132be7d22fSVladimir Kondratiev if (0 == left) { 5142be7d22fSVladimir Kondratiev wil_err(wil, "Firmware not ready\n"); 5152be7d22fSVladimir Kondratiev return -ETIME; 5162be7d22fSVladimir Kondratiev } else { 51715e23124SVladimir Kondratiev wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", 51815e23124SVladimir Kondratiev jiffies_to_msecs(to-left), wil->hw_version); 5192be7d22fSVladimir Kondratiev } 5202be7d22fSVladimir Kondratiev return 0; 5212be7d22fSVladimir Kondratiev } 5222be7d22fSVladimir Kondratiev 5232be7d22fSVladimir Kondratiev /* 5242be7d22fSVladimir Kondratiev * We reset all the structures, and we reset the UMAC. 5252be7d22fSVladimir Kondratiev * After calling this routine, you're expected to reload 5262be7d22fSVladimir Kondratiev * the firmware. 5272be7d22fSVladimir Kondratiev */ 5282be7d22fSVladimir Kondratiev int wil_reset(struct wil6210_priv *wil) 5292be7d22fSVladimir Kondratiev { 5302be7d22fSVladimir Kondratiev int rc; 5312be7d22fSVladimir Kondratiev 5329cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 5339cf10d62SVladimir Kondratiev 534097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 53573d839aeSVladimir Kondratiev WARN_ON(test_bit(wil_status_napi_en, &wil->status)); 536097638a0SVladimir Kondratiev 537097638a0SVladimir Kondratiev cancel_work_sync(&wil->disconnect_worker); 538097638a0SVladimir Kondratiev wil6210_disconnect(wil, NULL); 539097638a0SVladimir Kondratiev 5400fef1818SVladimir Kondratiev wil->status = 0; /* prevent NAPI from being scheduled */ 5410fef1818SVladimir Kondratiev 542ed6f9dc6SVladimir Kondratiev if (wil->scan_request) { 543ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "Abort scan_request 0x%p\n", 544ed6f9dc6SVladimir Kondratiev wil->scan_request); 545047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 546ed6f9dc6SVladimir Kondratiev cfg80211_scan_done(wil->scan_request, true); 547ed6f9dc6SVladimir Kondratiev wil->scan_request = NULL; 548ed6f9dc6SVladimir Kondratiev } 549ed6f9dc6SVladimir Kondratiev 550e4dbb093SVladimir Kondratiev wil_mask_irq(wil); 5512be7d22fSVladimir Kondratiev 552e08b5906SVladimir Kondratiev wmi_event_flush(wil); 553e08b5906SVladimir Kondratiev 554e08b5906SVladimir Kondratiev flush_workqueue(wil->wmi_wq_conn); 555e08b5906SVladimir Kondratiev flush_workqueue(wil->wmi_wq); 556e08b5906SVladimir Kondratiev 557bbb2adc7SVladimir Kondratiev rc = wil_target_reset(wil); 5588bf6adb9SVladimir Kondratiev wil_rx_fini(wil); 559bbb2adc7SVladimir Kondratiev if (rc) 560bbb2adc7SVladimir Kondratiev return rc; 561bbb2adc7SVladimir Kondratiev 562151a9706SVladimir Kondratiev if (!no_fw_load) { 563151a9706SVladimir Kondratiev wil_info(wil, "Use firmware <%s>\n", WIL_FW_NAME); 564151a9706SVladimir Kondratiev wil_halt_cpu(wil); 565151a9706SVladimir Kondratiev /* Loading f/w from the file */ 566151a9706SVladimir Kondratiev rc = wil_request_firmware(wil, WIL_FW_NAME); 567151a9706SVladimir Kondratiev if (rc) 568151a9706SVladimir Kondratiev return rc; 569151a9706SVladimir Kondratiev 570151a9706SVladimir Kondratiev /* clear any interrupts which on-card-firmware may have set */ 571151a9706SVladimir Kondratiev wil6210_clear_irq(wil); 572151a9706SVladimir Kondratiev { /* CAF_ICR - clear and mask */ 573151a9706SVladimir Kondratiev u32 a = HOSTADDR(RGF_CAF_ICR) + 574151a9706SVladimir Kondratiev offsetof(struct RGF_ICR, ICR); 575151a9706SVladimir Kondratiev u32 m = HOSTADDR(RGF_CAF_ICR) + 576151a9706SVladimir Kondratiev offsetof(struct RGF_ICR, IMV); 577151a9706SVladimir Kondratiev u32 icr = ioread32(wil->csr + a); 578151a9706SVladimir Kondratiev 579151a9706SVladimir Kondratiev iowrite32(icr, wil->csr + a); /* W1C */ 580151a9706SVladimir Kondratiev iowrite32(~0, wil->csr + m); 581151a9706SVladimir Kondratiev wmb(); /* wait for completion */ 582151a9706SVladimir Kondratiev } 583151a9706SVladimir Kondratiev wil_release_cpu(wil); 584151a9706SVladimir Kondratiev } else { 585151a9706SVladimir Kondratiev wil_info(wil, "Use firmware from on-card flash\n"); 586151a9706SVladimir Kondratiev } 5878bf6adb9SVladimir Kondratiev 5882be7d22fSVladimir Kondratiev /* init after reset */ 5892be7d22fSVladimir Kondratiev wil->pending_connect_cid = -1; 59016735d02SWolfram Sang reinit_completion(&wil->wmi_ready); 59159502647SDedy Lansky reinit_completion(&wil->wmi_call); 5922be7d22fSVladimir Kondratiev 593e4dbb093SVladimir Kondratiev wil_unmask_irq(wil); 5942be7d22fSVladimir Kondratiev 5952be7d22fSVladimir Kondratiev /* we just started MAC, wait for FW ready */ 5962be7d22fSVladimir Kondratiev rc = wil_wait_for_fw_ready(wil); 5972be7d22fSVladimir Kondratiev 5982be7d22fSVladimir Kondratiev return rc; 5992be7d22fSVladimir Kondratiev } 6002be7d22fSVladimir Kondratiev 601ed6f9dc6SVladimir Kondratiev void wil_fw_error_recovery(struct wil6210_priv *wil) 602ed6f9dc6SVladimir Kondratiev { 603ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "starting fw error recovery\n"); 604c33407a8SVladimir Kondratiev wil->recovery_state = fw_recovery_pending; 605ed6f9dc6SVladimir Kondratiev schedule_work(&wil->fw_error_worker); 606ed6f9dc6SVladimir Kondratiev } 6072be7d22fSVladimir Kondratiev 6082be7d22fSVladimir Kondratiev void wil_link_on(struct wil6210_priv *wil) 6092be7d22fSVladimir Kondratiev { 6102be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 6112be7d22fSVladimir Kondratiev 6127743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 6132be7d22fSVladimir Kondratiev 6142be7d22fSVladimir Kondratiev netif_carrier_on(ndev); 61555f8f680SVladimir Kondratiev wil_dbg_misc(wil, "netif_tx_wake : link on\n"); 6162be7d22fSVladimir Kondratiev netif_tx_wake_all_queues(ndev); 6172be7d22fSVladimir Kondratiev } 6182be7d22fSVladimir Kondratiev 6192be7d22fSVladimir Kondratiev void wil_link_off(struct wil6210_priv *wil) 6202be7d22fSVladimir Kondratiev { 6212be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 6222be7d22fSVladimir Kondratiev 6237743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 6242be7d22fSVladimir Kondratiev 6252be7d22fSVladimir Kondratiev netif_tx_stop_all_queues(ndev); 62655f8f680SVladimir Kondratiev wil_dbg_misc(wil, "netif_tx_stop : link off\n"); 6272be7d22fSVladimir Kondratiev netif_carrier_off(ndev); 6282be7d22fSVladimir Kondratiev } 6292be7d22fSVladimir Kondratiev 63073d839aeSVladimir Kondratiev int __wil_up(struct wil6210_priv *wil) 6312be7d22fSVladimir Kondratiev { 6322be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 6332be7d22fSVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 6342be7d22fSVladimir Kondratiev int rc; 6352be7d22fSVladimir Kondratiev 636097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 637097638a0SVladimir Kondratiev 6382be7d22fSVladimir Kondratiev rc = wil_reset(wil); 6392be7d22fSVladimir Kondratiev if (rc) 6402be7d22fSVladimir Kondratiev return rc; 6412be7d22fSVladimir Kondratiev 642e31b2562SVladimir Kondratiev /* Rx VRING. After MAC and beacon */ 643e31b2562SVladimir Kondratiev rc = wil_rx_init(wil); 644e31b2562SVladimir Kondratiev if (rc) 645e31b2562SVladimir Kondratiev return rc; 646e31b2562SVladimir Kondratiev 6472be7d22fSVladimir Kondratiev switch (wdev->iftype) { 6482be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION: 6497743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: STATION\n"); 6502be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 6512be7d22fSVladimir Kondratiev break; 6522be7d22fSVladimir Kondratiev case NL80211_IFTYPE_AP: 6537743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: AP\n"); 6542be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 6552be7d22fSVladimir Kondratiev break; 6562be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 6577743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: P2P_CLIENT\n"); 6582be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 6592be7d22fSVladimir Kondratiev break; 6602be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 6617743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: P2P_GO\n"); 6622be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 6632be7d22fSVladimir Kondratiev break; 6642be7d22fSVladimir Kondratiev case NL80211_IFTYPE_MONITOR: 6657743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: Monitor\n"); 6662be7d22fSVladimir Kondratiev ndev->type = ARPHRD_IEEE80211_RADIOTAP; 6672be7d22fSVladimir Kondratiev /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ 6682be7d22fSVladimir Kondratiev break; 6692be7d22fSVladimir Kondratiev default: 6702be7d22fSVladimir Kondratiev return -EOPNOTSUPP; 6712be7d22fSVladimir Kondratiev } 6722be7d22fSVladimir Kondratiev 6732be7d22fSVladimir Kondratiev /* MAC address - pre-requisite for other commands */ 6742be7d22fSVladimir Kondratiev wmi_set_mac_address(wil, ndev->dev_addr); 6752be7d22fSVladimir Kondratiev 67673d839aeSVladimir Kondratiev wil_dbg_misc(wil, "NAPI enable\n"); 677e0287c4aSVladimir Kondratiev napi_enable(&wil->napi_rx); 678e0287c4aSVladimir Kondratiev napi_enable(&wil->napi_tx); 6790fef1818SVladimir Kondratiev set_bit(wil_status_napi_en, &wil->status); 680e0287c4aSVladimir Kondratiev 681f772ebfbSVladimir Kondratiev if (wil->platform_ops.bus_request) 682f772ebfbSVladimir Kondratiev wil->platform_ops.bus_request(wil->platform_handle, 683f772ebfbSVladimir Kondratiev WIL_MAX_BUS_REQUEST_KBPS); 684f772ebfbSVladimir Kondratiev 6852be7d22fSVladimir Kondratiev return 0; 6862be7d22fSVladimir Kondratiev } 6872be7d22fSVladimir Kondratiev 6882be7d22fSVladimir Kondratiev int wil_up(struct wil6210_priv *wil) 6892be7d22fSVladimir Kondratiev { 6902be7d22fSVladimir Kondratiev int rc; 6912be7d22fSVladimir Kondratiev 6929cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 6939cf10d62SVladimir Kondratiev 6942be7d22fSVladimir Kondratiev mutex_lock(&wil->mutex); 6952be7d22fSVladimir Kondratiev rc = __wil_up(wil); 6962be7d22fSVladimir Kondratiev mutex_unlock(&wil->mutex); 6972be7d22fSVladimir Kondratiev 6982be7d22fSVladimir Kondratiev return rc; 6992be7d22fSVladimir Kondratiev } 7002be7d22fSVladimir Kondratiev 70173d839aeSVladimir Kondratiev int __wil_down(struct wil6210_priv *wil) 7022be7d22fSVladimir Kondratiev { 703f172b563SDedy Lansky int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS / 704f172b563SDedy Lansky WAIT_FOR_DISCONNECT_INTERVAL_MS; 705f172b563SDedy Lansky 706097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 707097638a0SVladimir Kondratiev 708f772ebfbSVladimir Kondratiev if (wil->platform_ops.bus_request) 709f772ebfbSVladimir Kondratiev wil->platform_ops.bus_request(wil->platform_handle, 0); 710f772ebfbSVladimir Kondratiev 71173d839aeSVladimir Kondratiev wil_disable_irq(wil); 71273d839aeSVladimir Kondratiev if (test_and_clear_bit(wil_status_napi_en, &wil->status)) { 713e0287c4aSVladimir Kondratiev napi_disable(&wil->napi_rx); 714e0287c4aSVladimir Kondratiev napi_disable(&wil->napi_tx); 71573d839aeSVladimir Kondratiev wil_dbg_misc(wil, "NAPI disable\n"); 71673d839aeSVladimir Kondratiev } 71773d839aeSVladimir Kondratiev wil_enable_irq(wil); 718e0287c4aSVladimir Kondratiev 7192be7d22fSVladimir Kondratiev if (wil->scan_request) { 7202a91d7d0SVladimir Kondratiev wil_dbg_misc(wil, "Abort scan_request 0x%p\n", 7212a91d7d0SVladimir Kondratiev wil->scan_request); 722047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 7232be7d22fSVladimir Kondratiev cfg80211_scan_done(wil->scan_request, true); 7242be7d22fSVladimir Kondratiev wil->scan_request = NULL; 7252be7d22fSVladimir Kondratiev } 7262be7d22fSVladimir Kondratiev 727f172b563SDedy Lansky if (test_bit(wil_status_fwconnected, &wil->status) || 728f172b563SDedy Lansky test_bit(wil_status_fwconnecting, &wil->status)) 729f172b563SDedy Lansky wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); 730f172b563SDedy Lansky 731f172b563SDedy Lansky /* make sure wil is idle (not connected) */ 732f172b563SDedy Lansky mutex_unlock(&wil->mutex); 733f172b563SDedy Lansky while (iter--) { 734f172b563SDedy Lansky int idle = !test_bit(wil_status_fwconnected, &wil->status) && 735f172b563SDedy Lansky !test_bit(wil_status_fwconnecting, &wil->status); 736f172b563SDedy Lansky if (idle) 737f172b563SDedy Lansky break; 738f172b563SDedy Lansky msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); 739f172b563SDedy Lansky } 740f172b563SDedy Lansky mutex_lock(&wil->mutex); 741f172b563SDedy Lansky 742f172b563SDedy Lansky if (!iter) 743f172b563SDedy Lansky wil_err(wil, "timeout waiting for idle FW/HW\n"); 744f172b563SDedy Lansky 7452be7d22fSVladimir Kondratiev wil_rx_fini(wil); 7462be7d22fSVladimir Kondratiev 7472be7d22fSVladimir Kondratiev return 0; 7482be7d22fSVladimir Kondratiev } 7492be7d22fSVladimir Kondratiev 7502be7d22fSVladimir Kondratiev int wil_down(struct wil6210_priv *wil) 7512be7d22fSVladimir Kondratiev { 7522be7d22fSVladimir Kondratiev int rc; 7532be7d22fSVladimir Kondratiev 7549cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 7559cf10d62SVladimir Kondratiev 756c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 7572be7d22fSVladimir Kondratiev mutex_lock(&wil->mutex); 7582be7d22fSVladimir Kondratiev rc = __wil_down(wil); 7592be7d22fSVladimir Kondratiev mutex_unlock(&wil->mutex); 7602be7d22fSVladimir Kondratiev 7612be7d22fSVladimir Kondratiev return rc; 7622be7d22fSVladimir Kondratiev } 7633df2cd36SVladimir Kondratiev 7643df2cd36SVladimir Kondratiev int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) 7653df2cd36SVladimir Kondratiev { 7663df2cd36SVladimir Kondratiev int i; 7673df2cd36SVladimir Kondratiev int rc = -ENOENT; 7683df2cd36SVladimir Kondratiev 7693df2cd36SVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { 7703df2cd36SVladimir Kondratiev if ((wil->sta[i].status != wil_sta_unused) && 771108d1eb6SVladimir Kondratiev ether_addr_equal(wil->sta[i].addr, mac)) { 7723df2cd36SVladimir Kondratiev rc = i; 7733df2cd36SVladimir Kondratiev break; 7743df2cd36SVladimir Kondratiev } 7753df2cd36SVladimir Kondratiev } 7763df2cd36SVladimir Kondratiev 7773df2cd36SVladimir Kondratiev return rc; 7783df2cd36SVladimir Kondratiev } 779