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