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 3678366f69SVladimir Kondratiev static unsigned int tx_interframe_timeout = 3778366f69SVladimir Kondratiev WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT; 38b6b1b0ecSVladimir Kondratiev 3978366f69SVladimir Kondratiev module_param(tx_interframe_timeout, uint, S_IRUGO); 4078366f69SVladimir Kondratiev MODULE_PARM_DESC(tx_interframe_timeout, 4178366f69SVladimir Kondratiev " Interrupt moderation TX interframe timeout, usecs."); 4278366f69SVladimir Kondratiev 4378366f69SVladimir Kondratiev static unsigned int rx_interframe_timeout = 4478366f69SVladimir Kondratiev WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT; 4578366f69SVladimir Kondratiev 4678366f69SVladimir Kondratiev module_param(rx_interframe_timeout, uint, S_IRUGO); 4778366f69SVladimir Kondratiev MODULE_PARM_DESC(rx_interframe_timeout, 4878366f69SVladimir Kondratiev " Interrupt moderation RX interframe timeout, usecs."); 4978366f69SVladimir Kondratiev 5078366f69SVladimir Kondratiev static unsigned int tx_max_burst_duration = 5178366f69SVladimir Kondratiev WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT; 5278366f69SVladimir Kondratiev 5378366f69SVladimir Kondratiev module_param(tx_max_burst_duration, uint, S_IRUGO); 5478366f69SVladimir Kondratiev MODULE_PARM_DESC(tx_max_burst_duration, 5578366f69SVladimir Kondratiev " Interrupt moderation TX max burst duration, usecs."); 5678366f69SVladimir Kondratiev 5778366f69SVladimir Kondratiev static unsigned int rx_max_burst_duration = 5878366f69SVladimir Kondratiev WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT; 5978366f69SVladimir Kondratiev 6078366f69SVladimir Kondratiev module_param(rx_max_burst_duration, uint, S_IRUGO); 6178366f69SVladimir Kondratiev MODULE_PARM_DESC(rx_max_burst_duration, 6278366f69SVladimir Kondratiev " Interrupt moderation RX max burst duration, usecs."); 6378366f69SVladimir Kondratiev 64ab954628SVladimir Kondratiev /* if not set via modparam, will be set to default value of 1/8 of 65ab954628SVladimir Kondratiev * rx ring size during init flow 66ab954628SVladimir Kondratiev */ 67ab954628SVladimir Kondratiev unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT; 68ab954628SVladimir Kondratiev module_param(rx_ring_overflow_thrsh, ushort, S_IRUGO); 69ab954628SVladimir Kondratiev MODULE_PARM_DESC(rx_ring_overflow_thrsh, 70ab954628SVladimir Kondratiev " RX ring overflow threshold in descriptors."); 71b6b1b0ecSVladimir Kondratiev 729a06bec9SVladimir Kondratiev /* We allow allocation of more than 1 page buffers to support large packets. 739a06bec9SVladimir Kondratiev * It is suboptimal behavior performance wise in case MTU above page size. 749a06bec9SVladimir Kondratiev */ 75c44690a1SVladimir Kondratiev unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; 769a06bec9SVladimir Kondratiev static int mtu_max_set(const char *val, const struct kernel_param *kp) 779a06bec9SVladimir Kondratiev { 789a06bec9SVladimir Kondratiev int ret; 799a06bec9SVladimir Kondratiev 809a06bec9SVladimir Kondratiev /* sets mtu_max directly. no need to restore it in case of 819a06bec9SVladimir Kondratiev * illegal value since we assume this will fail insmod 829a06bec9SVladimir Kondratiev */ 839a06bec9SVladimir Kondratiev ret = param_set_uint(val, kp); 849a06bec9SVladimir Kondratiev if (ret) 859a06bec9SVladimir Kondratiev return ret; 869a06bec9SVladimir Kondratiev 874590d812SVladimir Kondratiev if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU) 889a06bec9SVladimir Kondratiev ret = -EINVAL; 899a06bec9SVladimir Kondratiev 909a06bec9SVladimir Kondratiev return ret; 919a06bec9SVladimir Kondratiev } 929a06bec9SVladimir Kondratiev 939a06bec9SVladimir Kondratiev static struct kernel_param_ops mtu_max_ops = { 949a06bec9SVladimir Kondratiev .set = mtu_max_set, 959a06bec9SVladimir Kondratiev .get = param_get_uint, 969a06bec9SVladimir Kondratiev }; 979a06bec9SVladimir Kondratiev 989a06bec9SVladimir Kondratiev module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO); 999a06bec9SVladimir Kondratiev MODULE_PARM_DESC(mtu_max, " Max MTU value."); 1009a06bec9SVladimir Kondratiev 101d3762b40SVladimir Kondratiev static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT; 102d3762b40SVladimir Kondratiev static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT; 103d3762b40SVladimir Kondratiev 104d3762b40SVladimir Kondratiev static int ring_order_set(const char *val, const struct kernel_param *kp) 105d3762b40SVladimir Kondratiev { 106d3762b40SVladimir Kondratiev int ret; 107d3762b40SVladimir Kondratiev uint x; 108d3762b40SVladimir Kondratiev 109d3762b40SVladimir Kondratiev ret = kstrtouint(val, 0, &x); 110d3762b40SVladimir Kondratiev if (ret) 111d3762b40SVladimir Kondratiev return ret; 112d3762b40SVladimir Kondratiev 113d3762b40SVladimir Kondratiev if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX)) 114d3762b40SVladimir Kondratiev return -EINVAL; 115d3762b40SVladimir Kondratiev 116d3762b40SVladimir Kondratiev *((uint *)kp->arg) = x; 117d3762b40SVladimir Kondratiev 118d3762b40SVladimir Kondratiev return 0; 119d3762b40SVladimir Kondratiev } 120d3762b40SVladimir Kondratiev 121d3762b40SVladimir Kondratiev static struct kernel_param_ops ring_order_ops = { 122d3762b40SVladimir Kondratiev .set = ring_order_set, 123d3762b40SVladimir Kondratiev .get = param_get_uint, 124d3762b40SVladimir Kondratiev }; 125d3762b40SVladimir Kondratiev 126d3762b40SVladimir Kondratiev module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO); 127d3762b40SVladimir Kondratiev MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order"); 128d3762b40SVladimir Kondratiev module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO); 129d3762b40SVladimir Kondratiev MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order"); 130d3762b40SVladimir Kondratiev 131520d68e7SVladimir Kondratiev #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */ 132520d68e7SVladimir Kondratiev #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */ 133520d68e7SVladimir Kondratiev 1342be7d22fSVladimir Kondratiev /* 1352be7d22fSVladimir Kondratiev * Due to a hardware issue, 1362be7d22fSVladimir Kondratiev * one has to read/write to/from NIC in 32-bit chunks; 1372be7d22fSVladimir Kondratiev * regular memcpy_fromio and siblings will 1382be7d22fSVladimir Kondratiev * not work on 64-bit platform - it uses 64-bit transactions 1392be7d22fSVladimir Kondratiev * 1402be7d22fSVladimir Kondratiev * Force 32-bit transactions to enable NIC on 64-bit platforms 1412be7d22fSVladimir Kondratiev * 1422be7d22fSVladimir Kondratiev * To avoid byte swap on big endian host, __raw_{read|write}l 1432be7d22fSVladimir Kondratiev * should be used - {read|write}l would swap bytes to provide 1442be7d22fSVladimir Kondratiev * little endian on PCI value in host endianness. 1452be7d22fSVladimir Kondratiev */ 1462be7d22fSVladimir Kondratiev void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, 1472be7d22fSVladimir Kondratiev size_t count) 1482be7d22fSVladimir Kondratiev { 1492be7d22fSVladimir Kondratiev u32 *d = dst; 1502be7d22fSVladimir Kondratiev const volatile u32 __iomem *s = src; 1512be7d22fSVladimir Kondratiev 1522be7d22fSVladimir Kondratiev /* size_t is unsigned, if (count%4 != 0) it will wrap */ 1532be7d22fSVladimir Kondratiev for (count += 4; count > 4; count -= 4) 1542be7d22fSVladimir Kondratiev *d++ = __raw_readl(s++); 1552be7d22fSVladimir Kondratiev } 1562be7d22fSVladimir Kondratiev 1572be7d22fSVladimir Kondratiev void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, 1582be7d22fSVladimir Kondratiev size_t count) 1592be7d22fSVladimir Kondratiev { 1602be7d22fSVladimir Kondratiev volatile u32 __iomem *d = dst; 1612be7d22fSVladimir Kondratiev const u32 *s = src; 1622be7d22fSVladimir Kondratiev 1632be7d22fSVladimir Kondratiev for (count += 4; count > 4; count -= 4) 1642be7d22fSVladimir Kondratiev __raw_writel(*s++, d++); 1652be7d22fSVladimir Kondratiev } 1662be7d22fSVladimir Kondratiev 167b516fcc5SVladimir Kondratiev static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, 1684821e6d8SVladimir Kondratiev u16 reason_code, bool from_event) 1692be7d22fSVladimir Kondratiev { 17091886b0bSVladimir Kondratiev uint i; 171fc58f681SVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 172fc58f681SVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 173b4490f42SVladimir Kondratiev struct wil_sta_info *sta = &wil->sta[cid]; 1748fe59627SVladimir Kondratiev 175fc58f681SVladimir Kondratiev wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, 176fc58f681SVladimir Kondratiev sta->status); 1774d55a0a1SVladimir Kondratiev 178e58c9f70SVladimir Kondratiev sta->data_port_open = false; 1794d55a0a1SVladimir Kondratiev if (sta->status != wil_sta_unused) { 180b516fcc5SVladimir Kondratiev if (!from_event) 1814821e6d8SVladimir Kondratiev wmi_disconnect_sta(wil, sta->addr, reason_code); 182b516fcc5SVladimir Kondratiev 183fc58f681SVladimir Kondratiev switch (wdev->iftype) { 184fc58f681SVladimir Kondratiev case NL80211_IFTYPE_AP: 185fc58f681SVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 186fc58f681SVladimir Kondratiev /* AP-like interface */ 187fc58f681SVladimir Kondratiev cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); 188fc58f681SVladimir Kondratiev break; 189fc58f681SVladimir Kondratiev default: 190fc58f681SVladimir Kondratiev break; 191fc58f681SVladimir Kondratiev } 1924d55a0a1SVladimir Kondratiev sta->status = wil_sta_unused; 1934d55a0a1SVladimir Kondratiev } 1944d55a0a1SVladimir Kondratiev 195b4490f42SVladimir Kondratiev for (i = 0; i < WIL_STA_TID_NUM; i++) { 196ec81b5adSDedy Lansky struct wil_tid_ampdu_rx *r; 197ec81b5adSDedy Lansky unsigned long flags; 198ec81b5adSDedy Lansky 199ec81b5adSDedy Lansky spin_lock_irqsave(&sta->tid_rx_lock, flags); 200ec81b5adSDedy Lansky 201ec81b5adSDedy Lansky r = sta->tid_rx[i]; 202b4490f42SVladimir Kondratiev sta->tid_rx[i] = NULL; 203b4490f42SVladimir Kondratiev wil_tid_ampdu_rx_free(wil, r); 204ec81b5adSDedy Lansky 205ec81b5adSDedy Lansky spin_unlock_irqrestore(&sta->tid_rx_lock, flags); 206b4490f42SVladimir Kondratiev } 20791886b0bSVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { 20891886b0bSVladimir Kondratiev if (wil->vring2cid_tid[i][0] == cid) 20991886b0bSVladimir Kondratiev wil_vring_fini_tx(wil, i); 21091886b0bSVladimir Kondratiev } 21191886b0bSVladimir Kondratiev memset(&sta->stats, 0, sizeof(sta->stats)); 212b4490f42SVladimir Kondratiev } 213b4490f42SVladimir Kondratiev 214b516fcc5SVladimir Kondratiev static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, 2154821e6d8SVladimir Kondratiev u16 reason_code, bool from_event) 21691886b0bSVladimir Kondratiev { 21791886b0bSVladimir Kondratiev int cid = -ENOENT; 21891886b0bSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 21991886b0bSVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 22091886b0bSVladimir Kondratiev 22191886b0bSVladimir Kondratiev might_sleep(); 222100106d7SVladimir Kondratiev wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, 223100106d7SVladimir Kondratiev reason_code, from_event ? "+" : "-"); 22491886b0bSVladimir Kondratiev 225100106d7SVladimir Kondratiev /* Cases are: 226100106d7SVladimir Kondratiev * - disconnect single STA, still connected 227100106d7SVladimir Kondratiev * - disconnect single STA, already disconnected 228100106d7SVladimir Kondratiev * - disconnect all 229100106d7SVladimir Kondratiev * 230100106d7SVladimir Kondratiev * For "disconnect all", there are 2 options: 231100106d7SVladimir Kondratiev * - bssid == NULL 232100106d7SVladimir Kondratiev * - bssid is our MAC address 233100106d7SVladimir Kondratiev */ 234100106d7SVladimir Kondratiev if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) { 235100106d7SVladimir Kondratiev cid = wil_find_cid(wil, bssid); 236100106d7SVladimir Kondratiev wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", 237100106d7SVladimir Kondratiev bssid, cid, reason_code); 23891886b0bSVladimir Kondratiev if (cid >= 0) /* disconnect 1 peer */ 2394821e6d8SVladimir Kondratiev wil_disconnect_cid(wil, cid, reason_code, from_event); 240100106d7SVladimir Kondratiev } else { /* all */ 241100106d7SVladimir Kondratiev wil_dbg_misc(wil, "Disconnect all\n"); 24291886b0bSVladimir Kondratiev for (cid = 0; cid < WIL6210_MAX_CID; cid++) 2434821e6d8SVladimir Kondratiev wil_disconnect_cid(wil, cid, reason_code, from_event); 244100106d7SVladimir Kondratiev } 24591886b0bSVladimir Kondratiev 24691886b0bSVladimir Kondratiev /* link state */ 24791886b0bSVladimir Kondratiev switch (wdev->iftype) { 24891886b0bSVladimir Kondratiev case NL80211_IFTYPE_STATION: 24991886b0bSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 2502be7d22fSVladimir Kondratiev wil_link_off(wil); 2519419b6a2SVladimir Kondratiev if (test_bit(wil_status_fwconnected, wil->status)) { 2529419b6a2SVladimir Kondratiev clear_bit(wil_status_fwconnected, wil->status); 2534821e6d8SVladimir Kondratiev cfg80211_disconnected(ndev, reason_code, 2542be7d22fSVladimir Kondratiev NULL, 0, GFP_KERNEL); 2559419b6a2SVladimir Kondratiev } else if (test_bit(wil_status_fwconnecting, wil->status)) { 2562be7d22fSVladimir Kondratiev cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, 2572be7d22fSVladimir Kondratiev WLAN_STATUS_UNSPECIFIED_FAILURE, 2582be7d22fSVladimir Kondratiev GFP_KERNEL); 2592be7d22fSVladimir Kondratiev } 2609419b6a2SVladimir Kondratiev clear_bit(wil_status_fwconnecting, wil->status); 26191886b0bSVladimir Kondratiev break; 26291886b0bSVladimir Kondratiev default: 26391886b0bSVladimir Kondratiev break; 26491886b0bSVladimir Kondratiev } 2652be7d22fSVladimir Kondratiev } 2662be7d22fSVladimir Kondratiev 2672be7d22fSVladimir Kondratiev static void wil_disconnect_worker(struct work_struct *work) 2682be7d22fSVladimir Kondratiev { 2692be7d22fSVladimir Kondratiev struct wil6210_priv *wil = container_of(work, 2702be7d22fSVladimir Kondratiev struct wil6210_priv, disconnect_worker); 2712be7d22fSVladimir Kondratiev 272097638a0SVladimir Kondratiev mutex_lock(&wil->mutex); 2734821e6d8SVladimir Kondratiev _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false); 274097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex); 2752be7d22fSVladimir Kondratiev } 2762be7d22fSVladimir Kondratiev 2772be7d22fSVladimir Kondratiev static void wil_connect_timer_fn(ulong x) 2782be7d22fSVladimir Kondratiev { 2792be7d22fSVladimir Kondratiev struct wil6210_priv *wil = (void *)x; 2802be7d22fSVladimir Kondratiev 2817743882dSVladimir Kondratiev wil_dbg_misc(wil, "Connect timeout\n"); 2822be7d22fSVladimir Kondratiev 2832be7d22fSVladimir Kondratiev /* reschedule to thread context - disconnect won't 2842be7d22fSVladimir Kondratiev * run from atomic context 2852be7d22fSVladimir Kondratiev */ 2862be7d22fSVladimir Kondratiev schedule_work(&wil->disconnect_worker); 2872be7d22fSVladimir Kondratiev } 2882be7d22fSVladimir Kondratiev 289047e5d74SVladimir Kondratiev static void wil_scan_timer_fn(ulong x) 290047e5d74SVladimir Kondratiev { 291047e5d74SVladimir Kondratiev struct wil6210_priv *wil = (void *)x; 292047e5d74SVladimir Kondratiev 2939419b6a2SVladimir Kondratiev clear_bit(wil_status_fwready, wil->status); 294047e5d74SVladimir Kondratiev wil_err(wil, "Scan timeout detected, start fw error recovery\n"); 29560abbb6eSVladimir Kondratiev wil->recovery_state = fw_recovery_pending; 296047e5d74SVladimir Kondratiev schedule_work(&wil->fw_error_worker); 297047e5d74SVladimir Kondratiev } 298047e5d74SVladimir Kondratiev 299c33407a8SVladimir Kondratiev static int wil_wait_for_recovery(struct wil6210_priv *wil) 300c33407a8SVladimir Kondratiev { 301c33407a8SVladimir Kondratiev if (wait_event_interruptible(wil->wq, wil->recovery_state != 302c33407a8SVladimir Kondratiev fw_recovery_pending)) { 303c33407a8SVladimir Kondratiev wil_err(wil, "Interrupt, canceling recovery\n"); 304c33407a8SVladimir Kondratiev return -ERESTARTSYS; 305c33407a8SVladimir Kondratiev } 306c33407a8SVladimir Kondratiev if (wil->recovery_state != fw_recovery_running) { 307c33407a8SVladimir Kondratiev wil_info(wil, "Recovery cancelled\n"); 308c33407a8SVladimir Kondratiev return -EINTR; 309c33407a8SVladimir Kondratiev } 310c33407a8SVladimir Kondratiev wil_info(wil, "Proceed with recovery\n"); 311c33407a8SVladimir Kondratiev return 0; 312c33407a8SVladimir Kondratiev } 313c33407a8SVladimir Kondratiev 314c33407a8SVladimir Kondratiev void wil_set_recovery_state(struct wil6210_priv *wil, int state) 315c33407a8SVladimir Kondratiev { 316c33407a8SVladimir Kondratiev wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__, 317c33407a8SVladimir Kondratiev wil->recovery_state, state); 318c33407a8SVladimir Kondratiev 319c33407a8SVladimir Kondratiev wil->recovery_state = state; 320c33407a8SVladimir Kondratiev wake_up_interruptible(&wil->wq); 321c33407a8SVladimir Kondratiev } 322c33407a8SVladimir Kondratiev 323ed6f9dc6SVladimir Kondratiev static void wil_fw_error_worker(struct work_struct *work) 324ed6f9dc6SVladimir Kondratiev { 325c33407a8SVladimir Kondratiev struct wil6210_priv *wil = container_of(work, struct wil6210_priv, 326c33407a8SVladimir Kondratiev fw_error_worker); 327ed6f9dc6SVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 328ed6f9dc6SVladimir Kondratiev 329ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "fw error worker\n"); 330ed6f9dc6SVladimir Kondratiev 331cded9369SVladimir Kondratiev if (!netif_running(wil_to_ndev(wil))) { 332cded9369SVladimir Kondratiev wil_info(wil, "No recovery - interface is down\n"); 333cded9369SVladimir Kondratiev return; 334cded9369SVladimir Kondratiev } 335cded9369SVladimir Kondratiev 336fc219eedSVladimir Kondratiev /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO 337fc219eedSVladimir Kondratiev * passed since last recovery attempt 338fc219eedSVladimir Kondratiev */ 339fc219eedSVladimir Kondratiev if (time_is_after_jiffies(wil->last_fw_recovery + 340fc219eedSVladimir Kondratiev WIL6210_FW_RECOVERY_TO)) 341fc219eedSVladimir Kondratiev wil->recovery_count++; 342fc219eedSVladimir Kondratiev else 343fc219eedSVladimir Kondratiev wil->recovery_count = 1; /* fw was alive for a long time */ 344fc219eedSVladimir Kondratiev 345fc219eedSVladimir Kondratiev if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { 346fc219eedSVladimir Kondratiev wil_err(wil, "too many recovery attempts (%d), giving up\n", 347fc219eedSVladimir Kondratiev wil->recovery_count); 348fc219eedSVladimir Kondratiev return; 349fc219eedSVladimir Kondratiev } 350fc219eedSVladimir Kondratiev 351fc219eedSVladimir Kondratiev wil->last_fw_recovery = jiffies; 352fc219eedSVladimir Kondratiev 3539c3bde56SVladimir Kondratiev mutex_lock(&wil->mutex); 354ed6f9dc6SVladimir Kondratiev switch (wdev->iftype) { 355ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_STATION: 356ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 357ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_MONITOR: 358c33407a8SVladimir Kondratiev wil_info(wil, "fw error recovery requested (try %d)...\n", 359fc219eedSVladimir Kondratiev wil->recovery_count); 360c33407a8SVladimir Kondratiev if (!no_fw_recovery) 361c33407a8SVladimir Kondratiev wil->recovery_state = fw_recovery_running; 362c33407a8SVladimir Kondratiev if (0 != wil_wait_for_recovery(wil)) 363c33407a8SVladimir Kondratiev break; 364c33407a8SVladimir Kondratiev 36573d839aeSVladimir Kondratiev __wil_down(wil); 36673d839aeSVladimir Kondratiev __wil_up(wil); 367ed6f9dc6SVladimir Kondratiev break; 368ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_AP: 369ed6f9dc6SVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 370e240537bSVladimir Kondratiev wil_info(wil, "No recovery for AP-like interface\n"); 371ed6f9dc6SVladimir Kondratiev /* recovery in these modes is done by upper layers */ 372ed6f9dc6SVladimir Kondratiev break; 373ed6f9dc6SVladimir Kondratiev default: 374e240537bSVladimir Kondratiev wil_err(wil, "No recovery - unknown interface type %d\n", 375e240537bSVladimir Kondratiev wdev->iftype); 376ed6f9dc6SVladimir Kondratiev break; 377ed6f9dc6SVladimir Kondratiev } 3789c3bde56SVladimir Kondratiev mutex_unlock(&wil->mutex); 379ed6f9dc6SVladimir Kondratiev } 380ed6f9dc6SVladimir Kondratiev 3819a177384SVladimir Kondratiev static int wil_find_free_vring(struct wil6210_priv *wil) 3829a177384SVladimir Kondratiev { 3839a177384SVladimir Kondratiev int i; 3848fe59627SVladimir Kondratiev 3859a177384SVladimir Kondratiev for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { 3869a177384SVladimir Kondratiev if (!wil->vring_tx[i].va) 3879a177384SVladimir Kondratiev return i; 3889a177384SVladimir Kondratiev } 3899a177384SVladimir Kondratiev return -EINVAL; 3909a177384SVladimir Kondratiev } 3919a177384SVladimir Kondratiev 392d81079f1SVladimir Kondratiev static void wil_connect_worker(struct work_struct *work) 393d81079f1SVladimir Kondratiev { 394d81079f1SVladimir Kondratiev int rc; 395d81079f1SVladimir Kondratiev struct wil6210_priv *wil = container_of(work, struct wil6210_priv, 396d81079f1SVladimir Kondratiev connect_worker); 397d81079f1SVladimir Kondratiev int cid = wil->pending_connect_cid; 3989a177384SVladimir Kondratiev int ringid = wil_find_free_vring(wil); 399d81079f1SVladimir Kondratiev 400d81079f1SVladimir Kondratiev if (cid < 0) { 401d81079f1SVladimir Kondratiev wil_err(wil, "No connection pending\n"); 402d81079f1SVladimir Kondratiev return; 403d81079f1SVladimir Kondratiev } 404d81079f1SVladimir Kondratiev 405d81079f1SVladimir Kondratiev wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); 406d81079f1SVladimir Kondratiev 407d3762b40SVladimir Kondratiev rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); 408d81079f1SVladimir Kondratiev wil->pending_connect_cid = -1; 4093df2cd36SVladimir Kondratiev if (rc == 0) { 4103df2cd36SVladimir Kondratiev wil->sta[cid].status = wil_sta_connected; 411d81079f1SVladimir Kondratiev wil_link_on(wil); 4123df2cd36SVladimir Kondratiev } else { 4133df2cd36SVladimir Kondratiev wil->sta[cid].status = wil_sta_unused; 4143df2cd36SVladimir Kondratiev } 415d81079f1SVladimir Kondratiev } 416d81079f1SVladimir Kondratiev 4172be7d22fSVladimir Kondratiev int wil_priv_init(struct wil6210_priv *wil) 4182be7d22fSVladimir Kondratiev { 419ec81b5adSDedy Lansky uint i; 420ec81b5adSDedy Lansky 4217743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 4222be7d22fSVladimir Kondratiev 4233df2cd36SVladimir Kondratiev memset(wil->sta, 0, sizeof(wil->sta)); 424ec81b5adSDedy Lansky for (i = 0; i < WIL6210_MAX_CID; i++) 425ec81b5adSDedy Lansky spin_lock_init(&wil->sta[i].tid_rx_lock); 4263df2cd36SVladimir Kondratiev 4272be7d22fSVladimir Kondratiev mutex_init(&wil->mutex); 4282be7d22fSVladimir Kondratiev mutex_init(&wil->wmi_mutex); 4293277213fSVladimir Kondratiev mutex_init(&wil->back_rx_mutex); 4303a124ed6SVladimir Kondratiev mutex_init(&wil->back_tx_mutex); 4312be7d22fSVladimir Kondratiev 4322be7d22fSVladimir Kondratiev init_completion(&wil->wmi_ready); 43359502647SDedy Lansky init_completion(&wil->wmi_call); 4342be7d22fSVladimir Kondratiev 4352be7d22fSVladimir Kondratiev wil->pending_connect_cid = -1; 4362be7d22fSVladimir Kondratiev setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); 437047e5d74SVladimir Kondratiev setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); 4382be7d22fSVladimir Kondratiev 439d81079f1SVladimir Kondratiev INIT_WORK(&wil->connect_worker, wil_connect_worker); 4402be7d22fSVladimir Kondratiev INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); 4412be7d22fSVladimir Kondratiev INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); 442ed6f9dc6SVladimir Kondratiev INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); 4433277213fSVladimir Kondratiev INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker); 4443a124ed6SVladimir Kondratiev INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker); 4452be7d22fSVladimir Kondratiev 4462be7d22fSVladimir Kondratiev INIT_LIST_HEAD(&wil->pending_wmi_ev); 4473277213fSVladimir Kondratiev INIT_LIST_HEAD(&wil->back_rx_pending); 4483a124ed6SVladimir Kondratiev INIT_LIST_HEAD(&wil->back_tx_pending); 4492be7d22fSVladimir Kondratiev spin_lock_init(&wil->wmi_ev_lock); 450c33407a8SVladimir Kondratiev init_waitqueue_head(&wil->wq); 4512be7d22fSVladimir Kondratiev 4522be7d22fSVladimir Kondratiev wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); 4532be7d22fSVladimir Kondratiev if (!wil->wmi_wq) 4542be7d22fSVladimir Kondratiev return -EAGAIN; 4552be7d22fSVladimir Kondratiev 4563277213fSVladimir Kondratiev wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service"); 4573277213fSVladimir Kondratiev if (!wil->wq_service) 4583277213fSVladimir Kondratiev goto out_wmi_wq; 4592be7d22fSVladimir Kondratiev 460fc219eedSVladimir Kondratiev wil->last_fw_recovery = jiffies; 46178366f69SVladimir Kondratiev wil->tx_interframe_timeout = tx_interframe_timeout; 46278366f69SVladimir Kondratiev wil->rx_interframe_timeout = rx_interframe_timeout; 46378366f69SVladimir Kondratiev wil->tx_max_burst_duration = tx_max_burst_duration; 46478366f69SVladimir Kondratiev wil->rx_max_burst_duration = rx_max_burst_duration; 465fc219eedSVladimir Kondratiev 466ab954628SVladimir Kondratiev if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) 467ab954628SVladimir Kondratiev rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; 4682be7d22fSVladimir Kondratiev return 0; 4693277213fSVladimir Kondratiev 4703277213fSVladimir Kondratiev out_wmi_wq: 4713277213fSVladimir Kondratiev destroy_workqueue(wil->wmi_wq); 4723277213fSVladimir Kondratiev 4733277213fSVladimir Kondratiev return -EAGAIN; 4742be7d22fSVladimir Kondratiev } 4752be7d22fSVladimir Kondratiev 476b516fcc5SVladimir Kondratiev /** 477b516fcc5SVladimir Kondratiev * wil6210_disconnect - disconnect one connection 478b516fcc5SVladimir Kondratiev * @wil: driver context 479b516fcc5SVladimir Kondratiev * @bssid: peer to disconnect, NULL to disconnect all 4804821e6d8SVladimir Kondratiev * @reason_code: Reason code for the Disassociation frame 481b516fcc5SVladimir Kondratiev * @from_event: whether is invoked from FW event handler 482b516fcc5SVladimir Kondratiev * 483b516fcc5SVladimir Kondratiev * Disconnect and release associated resources. If invoked not from the 484b516fcc5SVladimir Kondratiev * FW event handler, issue WMI command(s) to trigger MAC disconnect. 485b516fcc5SVladimir Kondratiev */ 486b516fcc5SVladimir Kondratiev void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, 4874821e6d8SVladimir Kondratiev u16 reason_code, bool from_event) 4882be7d22fSVladimir Kondratiev { 4899cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 4909cf10d62SVladimir Kondratiev 4912be7d22fSVladimir Kondratiev del_timer_sync(&wil->connect_timer); 4924821e6d8SVladimir Kondratiev _wil6210_disconnect(wil, bssid, reason_code, from_event); 4932be7d22fSVladimir Kondratiev } 4942be7d22fSVladimir Kondratiev 4952be7d22fSVladimir Kondratiev void wil_priv_deinit(struct wil6210_priv *wil) 4962be7d22fSVladimir Kondratiev { 4979cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 4989cf10d62SVladimir Kondratiev 499c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 500047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 5012be7d22fSVladimir Kondratiev cancel_work_sync(&wil->disconnect_worker); 502ed6f9dc6SVladimir Kondratiev cancel_work_sync(&wil->fw_error_worker); 503097638a0SVladimir Kondratiev mutex_lock(&wil->mutex); 5044821e6d8SVladimir Kondratiev wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); 505097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex); 5062be7d22fSVladimir Kondratiev wmi_event_flush(wil); 5073277213fSVladimir Kondratiev wil_back_rx_flush(wil); 5083277213fSVladimir Kondratiev cancel_work_sync(&wil->back_rx_worker); 5093a124ed6SVladimir Kondratiev wil_back_tx_flush(wil); 5103a124ed6SVladimir Kondratiev cancel_work_sync(&wil->back_tx_worker); 5113277213fSVladimir Kondratiev destroy_workqueue(wil->wq_service); 5122be7d22fSVladimir Kondratiev destroy_workqueue(wil->wmi_wq); 5132be7d22fSVladimir Kondratiev } 5142be7d22fSVladimir Kondratiev 515151a9706SVladimir Kondratiev /* target operations */ 516151a9706SVladimir Kondratiev /* register read */ 517151a9706SVladimir Kondratiev #define R(a) ioread32(wil->csr + HOSTADDR(a)) 518151a9706SVladimir Kondratiev /* register write. wmb() to make sure it is completed */ 519151a9706SVladimir Kondratiev #define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0) 520151a9706SVladimir Kondratiev /* register set = read, OR, write */ 521151a9706SVladimir Kondratiev #define S(a, v) W(a, R(a) | v) 522151a9706SVladimir Kondratiev /* register clear = read, AND with inverted, write */ 523151a9706SVladimir Kondratiev #define C(a, v) W(a, R(a) & ~v) 524151a9706SVladimir Kondratiev 525151a9706SVladimir Kondratiev static inline void wil_halt_cpu(struct wil6210_priv *wil) 526151a9706SVladimir Kondratiev { 527151a9706SVladimir Kondratiev W(RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); 528151a9706SVladimir Kondratiev W(RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST); 529151a9706SVladimir Kondratiev } 530151a9706SVladimir Kondratiev 531151a9706SVladimir Kondratiev static inline void wil_release_cpu(struct wil6210_priv *wil) 532151a9706SVladimir Kondratiev { 533151a9706SVladimir Kondratiev /* Start CPU */ 534151a9706SVladimir Kondratiev W(RGF_USER_USER_CPU_0, 1); 535151a9706SVladimir Kondratiev } 536151a9706SVladimir Kondratiev 537bbb2adc7SVladimir Kondratiev static int wil_target_reset(struct wil6210_priv *wil) 5382be7d22fSVladimir Kondratiev { 53998a65b59SVladimir Kondratiev int delay = 0; 54048516298SVladimir Kondratiev u32 x; 5411aeda13bSVladimir Kondratiev bool is_reset_v2 = test_bit(hw_capability_reset_v2, 5421aeda13bSVladimir Kondratiev wil->hw_capabilities); 54336b10a72SVladimir Kondratiev 5441aeda13bSVladimir Kondratiev wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); 5456508281bSVladimir Kondratiev 5466508281bSVladimir Kondratiev /* Clear MAC link up */ 5476508281bSVladimir Kondratiev S(RGF_HP_CTRL, BIT(15)); 548151a9706SVladimir Kondratiev S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD); 549151a9706SVladimir Kondratiev S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST); 550151a9706SVladimir Kondratiev 551151a9706SVladimir Kondratiev wil_halt_cpu(wil); 5522be7d22fSVladimir Kondratiev 553cce47711SVladimir Kondratiev /* Clear Fw Download notification */ 554cce47711SVladimir Kondratiev C(RGF_USER_USAGE_6, BIT(0)); 555cce47711SVladimir Kondratiev 5561aeda13bSVladimir Kondratiev if (is_reset_v2) { 55748516298SVladimir Kondratiev S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN); 55848516298SVladimir Kondratiev /* XTAL stabilization should take about 3ms */ 55948516298SVladimir Kondratiev usleep_range(5000, 7000); 56048516298SVladimir Kondratiev x = R(RGF_CAF_PLL_LOCK_STATUS); 56148516298SVladimir Kondratiev if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) { 56248516298SVladimir Kondratiev wil_err(wil, "Xtal stabilization timeout\n" 56348516298SVladimir Kondratiev "RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x); 56448516298SVladimir Kondratiev return -ETIME; 56548516298SVladimir Kondratiev } 56648516298SVladimir Kondratiev /* switch 10k to XTAL*/ 56748516298SVladimir Kondratiev C(RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF); 56848516298SVladimir Kondratiev /* 40 MHz */ 56948516298SVladimir Kondratiev C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); 57048516298SVladimir Kondratiev 5716508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); 572151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf); 5736508281bSVladimir Kondratiev } 5746508281bSVladimir Kondratiev 5752be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); 5762be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); 5771aeda13bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 5781aeda13bSVladimir Kondratiev is_reset_v2 ? 0x000000f0 : 0x00000170); 579151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00); 5802be7d22fSVladimir Kondratiev 5811aeda13bSVladimir Kondratiev if (is_reset_v2) { 5826508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); 583151a9706SVladimir Kondratiev W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0); 5846508281bSVladimir Kondratiev } 5856508281bSVladimir Kondratiev 5862be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); 5872be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); 5882be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); 5892be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); 5902be7d22fSVladimir Kondratiev 5911aeda13bSVladimir Kondratiev if (is_reset_v2) { 5926508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); 5936508281bSVladimir Kondratiev /* reset A2 PCIE AHB */ 5946508281bSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); 5956508281bSVladimir Kondratiev } else { 5962be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); 59717123991SVladimir Kondratiev W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); 59836b10a72SVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); 59936b10a72SVladimir Kondratiev } 6006508281bSVladimir Kondratiev 6016508281bSVladimir Kondratiev /* TODO: check order here!!! Erez code is different */ 6022be7d22fSVladimir Kondratiev W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); 6032be7d22fSVladimir Kondratiev 604520d68e7SVladimir Kondratiev /* wait until device ready. typical time is 200..250 msec */ 60536b10a72SVladimir Kondratiev do { 606520d68e7SVladimir Kondratiev msleep(RST_DELAY); 60748516298SVladimir Kondratiev x = R(RGF_USER_HW_MACHINE_STATE); 608520d68e7SVladimir Kondratiev if (delay++ > RST_COUNT) { 609d28bcc30SVladimir Kondratiev wil_err(wil, "Reset not completed, hw_state 0x%08x\n", 61048516298SVladimir Kondratiev x); 611bbb2adc7SVladimir Kondratiev return -ETIME; 61236b10a72SVladimir Kondratiev } 61348516298SVladimir Kondratiev } while (x != HW_MACHINE_BOOT_DONE); 61436b10a72SVladimir Kondratiev 6151aeda13bSVladimir Kondratiev if (!is_reset_v2) 61617123991SVladimir Kondratiev W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); 61736b10a72SVladimir Kondratiev 618972072aaSVladimir Kondratiev C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); 619972072aaSVladimir Kondratiev 620520d68e7SVladimir Kondratiev wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); 621bbb2adc7SVladimir Kondratiev return 0; 622151a9706SVladimir Kondratiev } 6232be7d22fSVladimir Kondratiev 62436b10a72SVladimir Kondratiev #undef R 6252be7d22fSVladimir Kondratiev #undef W 6262be7d22fSVladimir Kondratiev #undef S 627972072aaSVladimir Kondratiev #undef C 6282be7d22fSVladimir Kondratiev 6292be7d22fSVladimir Kondratiev void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) 6302be7d22fSVladimir Kondratiev { 6312be7d22fSVladimir Kondratiev le32_to_cpus(&r->base); 6322be7d22fSVladimir Kondratiev le16_to_cpus(&r->entry_size); 6332be7d22fSVladimir Kondratiev le16_to_cpus(&r->size); 6342be7d22fSVladimir Kondratiev le32_to_cpus(&r->tail); 6352be7d22fSVladimir Kondratiev le32_to_cpus(&r->head); 6362be7d22fSVladimir Kondratiev } 6372be7d22fSVladimir Kondratiev 6382be7d22fSVladimir Kondratiev static int wil_wait_for_fw_ready(struct wil6210_priv *wil) 6392be7d22fSVladimir Kondratiev { 6402be7d22fSVladimir Kondratiev ulong to = msecs_to_jiffies(1000); 6412be7d22fSVladimir Kondratiev ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); 6428fe59627SVladimir Kondratiev 6432be7d22fSVladimir Kondratiev if (0 == left) { 6442be7d22fSVladimir Kondratiev wil_err(wil, "Firmware not ready\n"); 6452be7d22fSVladimir Kondratiev return -ETIME; 6462be7d22fSVladimir Kondratiev } else { 64715e23124SVladimir Kondratiev wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", 64815e23124SVladimir Kondratiev jiffies_to_msecs(to-left), wil->hw_version); 6492be7d22fSVladimir Kondratiev } 6502be7d22fSVladimir Kondratiev return 0; 6512be7d22fSVladimir Kondratiev } 6522be7d22fSVladimir Kondratiev 6532be7d22fSVladimir Kondratiev /* 6542be7d22fSVladimir Kondratiev * We reset all the structures, and we reset the UMAC. 6552be7d22fSVladimir Kondratiev * After calling this routine, you're expected to reload 6562be7d22fSVladimir Kondratiev * the firmware. 6572be7d22fSVladimir Kondratiev */ 6582be7d22fSVladimir Kondratiev int wil_reset(struct wil6210_priv *wil) 6592be7d22fSVladimir Kondratiev { 6602be7d22fSVladimir Kondratiev int rc; 6612be7d22fSVladimir Kondratiev 6629cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 6639cf10d62SVladimir Kondratiev 6641aeda13bSVladimir Kondratiev if (wil->hw_version == HW_VER_UNKNOWN) 6651aeda13bSVladimir Kondratiev return -ENODEV; 6661aeda13bSVladimir Kondratiev 667097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 6689419b6a2SVladimir Kondratiev WARN_ON(test_bit(wil_status_napi_en, wil->status)); 669097638a0SVladimir Kondratiev 670097638a0SVladimir Kondratiev cancel_work_sync(&wil->disconnect_worker); 6714821e6d8SVladimir Kondratiev wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); 672097638a0SVladimir Kondratiev 6739419b6a2SVladimir Kondratiev /* prevent NAPI from being scheduled */ 6749419b6a2SVladimir Kondratiev bitmap_zero(wil->status, wil_status_last); 6750fef1818SVladimir Kondratiev 676ed6f9dc6SVladimir Kondratiev if (wil->scan_request) { 677ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "Abort scan_request 0x%p\n", 678ed6f9dc6SVladimir Kondratiev wil->scan_request); 679047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 680ed6f9dc6SVladimir Kondratiev cfg80211_scan_done(wil->scan_request, true); 681ed6f9dc6SVladimir Kondratiev wil->scan_request = NULL; 682ed6f9dc6SVladimir Kondratiev } 683ed6f9dc6SVladimir Kondratiev 684e4dbb093SVladimir Kondratiev wil_mask_irq(wil); 6852be7d22fSVladimir Kondratiev 686e08b5906SVladimir Kondratiev wmi_event_flush(wil); 687e08b5906SVladimir Kondratiev 6883277213fSVladimir Kondratiev flush_workqueue(wil->wq_service); 689e08b5906SVladimir Kondratiev flush_workqueue(wil->wmi_wq); 690e08b5906SVladimir Kondratiev 691bbb2adc7SVladimir Kondratiev rc = wil_target_reset(wil); 6928bf6adb9SVladimir Kondratiev wil_rx_fini(wil); 693bbb2adc7SVladimir Kondratiev if (rc) 694bbb2adc7SVladimir Kondratiev return rc; 695bbb2adc7SVladimir Kondratiev 696151a9706SVladimir Kondratiev if (!no_fw_load) { 697151a9706SVladimir Kondratiev wil_info(wil, "Use firmware <%s>\n", WIL_FW_NAME); 698151a9706SVladimir Kondratiev wil_halt_cpu(wil); 699151a9706SVladimir Kondratiev /* Loading f/w from the file */ 700151a9706SVladimir Kondratiev rc = wil_request_firmware(wil, WIL_FW_NAME); 701151a9706SVladimir Kondratiev if (rc) 702151a9706SVladimir Kondratiev return rc; 703151a9706SVladimir Kondratiev 704151a9706SVladimir Kondratiev /* clear any interrupts which on-card-firmware may have set */ 705151a9706SVladimir Kondratiev wil6210_clear_irq(wil); 706151a9706SVladimir Kondratiev { /* CAF_ICR - clear and mask */ 707151a9706SVladimir Kondratiev u32 a = HOSTADDR(RGF_CAF_ICR) + 708151a9706SVladimir Kondratiev offsetof(struct RGF_ICR, ICR); 709151a9706SVladimir Kondratiev u32 m = HOSTADDR(RGF_CAF_ICR) + 710151a9706SVladimir Kondratiev offsetof(struct RGF_ICR, IMV); 711151a9706SVladimir Kondratiev u32 icr = ioread32(wil->csr + a); 712151a9706SVladimir Kondratiev 713151a9706SVladimir Kondratiev iowrite32(icr, wil->csr + a); /* W1C */ 714151a9706SVladimir Kondratiev iowrite32(~0, wil->csr + m); 715151a9706SVladimir Kondratiev wmb(); /* wait for completion */ 716151a9706SVladimir Kondratiev } 717151a9706SVladimir Kondratiev wil_release_cpu(wil); 718151a9706SVladimir Kondratiev } else { 719151a9706SVladimir Kondratiev wil_info(wil, "Use firmware from on-card flash\n"); 720151a9706SVladimir Kondratiev } 7218bf6adb9SVladimir Kondratiev 7222be7d22fSVladimir Kondratiev /* init after reset */ 7232be7d22fSVladimir Kondratiev wil->pending_connect_cid = -1; 72416735d02SWolfram Sang reinit_completion(&wil->wmi_ready); 72559502647SDedy Lansky reinit_completion(&wil->wmi_call); 7262be7d22fSVladimir Kondratiev 72778366f69SVladimir Kondratiev wil_configure_interrupt_moderation(wil); 728e4dbb093SVladimir Kondratiev wil_unmask_irq(wil); 7292be7d22fSVladimir Kondratiev 7302be7d22fSVladimir Kondratiev /* we just started MAC, wait for FW ready */ 7312be7d22fSVladimir Kondratiev rc = wil_wait_for_fw_ready(wil); 7322be7d22fSVladimir Kondratiev 7332be7d22fSVladimir Kondratiev return rc; 7342be7d22fSVladimir Kondratiev } 7352be7d22fSVladimir Kondratiev 736ed6f9dc6SVladimir Kondratiev void wil_fw_error_recovery(struct wil6210_priv *wil) 737ed6f9dc6SVladimir Kondratiev { 738ed6f9dc6SVladimir Kondratiev wil_dbg_misc(wil, "starting fw error recovery\n"); 739c33407a8SVladimir Kondratiev wil->recovery_state = fw_recovery_pending; 740ed6f9dc6SVladimir Kondratiev schedule_work(&wil->fw_error_worker); 741ed6f9dc6SVladimir Kondratiev } 7422be7d22fSVladimir Kondratiev 7432be7d22fSVladimir Kondratiev void wil_link_on(struct wil6210_priv *wil) 7442be7d22fSVladimir Kondratiev { 7452be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 7462be7d22fSVladimir Kondratiev 7477743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 7482be7d22fSVladimir Kondratiev 7492be7d22fSVladimir Kondratiev netif_carrier_on(ndev); 75055f8f680SVladimir Kondratiev wil_dbg_misc(wil, "netif_tx_wake : link on\n"); 7512be7d22fSVladimir Kondratiev netif_tx_wake_all_queues(ndev); 7522be7d22fSVladimir Kondratiev } 7532be7d22fSVladimir Kondratiev 7542be7d22fSVladimir Kondratiev void wil_link_off(struct wil6210_priv *wil) 7552be7d22fSVladimir Kondratiev { 7562be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 7572be7d22fSVladimir Kondratiev 7587743882dSVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 7592be7d22fSVladimir Kondratiev 7602be7d22fSVladimir Kondratiev netif_tx_stop_all_queues(ndev); 76155f8f680SVladimir Kondratiev wil_dbg_misc(wil, "netif_tx_stop : link off\n"); 7622be7d22fSVladimir Kondratiev netif_carrier_off(ndev); 7632be7d22fSVladimir Kondratiev } 7642be7d22fSVladimir Kondratiev 76573d839aeSVladimir Kondratiev int __wil_up(struct wil6210_priv *wil) 7662be7d22fSVladimir Kondratiev { 7672be7d22fSVladimir Kondratiev struct net_device *ndev = wil_to_ndev(wil); 7682be7d22fSVladimir Kondratiev struct wireless_dev *wdev = wil->wdev; 7692be7d22fSVladimir Kondratiev int rc; 7702be7d22fSVladimir Kondratiev 771097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 772097638a0SVladimir Kondratiev 7732be7d22fSVladimir Kondratiev rc = wil_reset(wil); 7742be7d22fSVladimir Kondratiev if (rc) 7752be7d22fSVladimir Kondratiev return rc; 7762be7d22fSVladimir Kondratiev 777e31b2562SVladimir Kondratiev /* Rx VRING. After MAC and beacon */ 778d3762b40SVladimir Kondratiev rc = wil_rx_init(wil, 1 << rx_ring_order); 779e31b2562SVladimir Kondratiev if (rc) 780e31b2562SVladimir Kondratiev return rc; 781e31b2562SVladimir Kondratiev 7822be7d22fSVladimir Kondratiev switch (wdev->iftype) { 7832be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION: 7847743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: STATION\n"); 7852be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 7862be7d22fSVladimir Kondratiev break; 7872be7d22fSVladimir Kondratiev case NL80211_IFTYPE_AP: 7887743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: AP\n"); 7892be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 7902be7d22fSVladimir Kondratiev break; 7912be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 7927743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: P2P_CLIENT\n"); 7932be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 7942be7d22fSVladimir Kondratiev break; 7952be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 7967743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: P2P_GO\n"); 7972be7d22fSVladimir Kondratiev ndev->type = ARPHRD_ETHER; 7982be7d22fSVladimir Kondratiev break; 7992be7d22fSVladimir Kondratiev case NL80211_IFTYPE_MONITOR: 8007743882dSVladimir Kondratiev wil_dbg_misc(wil, "type: Monitor\n"); 8012be7d22fSVladimir Kondratiev ndev->type = ARPHRD_IEEE80211_RADIOTAP; 8022be7d22fSVladimir Kondratiev /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ 8032be7d22fSVladimir Kondratiev break; 8042be7d22fSVladimir Kondratiev default: 8052be7d22fSVladimir Kondratiev return -EOPNOTSUPP; 8062be7d22fSVladimir Kondratiev } 8072be7d22fSVladimir Kondratiev 8082be7d22fSVladimir Kondratiev /* MAC address - pre-requisite for other commands */ 8092be7d22fSVladimir Kondratiev wmi_set_mac_address(wil, ndev->dev_addr); 8102be7d22fSVladimir Kondratiev 81173d839aeSVladimir Kondratiev wil_dbg_misc(wil, "NAPI enable\n"); 812e0287c4aSVladimir Kondratiev napi_enable(&wil->napi_rx); 813e0287c4aSVladimir Kondratiev napi_enable(&wil->napi_tx); 8149419b6a2SVladimir Kondratiev set_bit(wil_status_napi_en, wil->status); 815e0287c4aSVladimir Kondratiev 816f772ebfbSVladimir Kondratiev if (wil->platform_ops.bus_request) 817f772ebfbSVladimir Kondratiev wil->platform_ops.bus_request(wil->platform_handle, 818f772ebfbSVladimir Kondratiev WIL_MAX_BUS_REQUEST_KBPS); 819f772ebfbSVladimir Kondratiev 8202be7d22fSVladimir Kondratiev return 0; 8212be7d22fSVladimir Kondratiev } 8222be7d22fSVladimir Kondratiev 8232be7d22fSVladimir Kondratiev int wil_up(struct wil6210_priv *wil) 8242be7d22fSVladimir Kondratiev { 8252be7d22fSVladimir Kondratiev int rc; 8262be7d22fSVladimir Kondratiev 8279cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 8289cf10d62SVladimir Kondratiev 8292be7d22fSVladimir Kondratiev mutex_lock(&wil->mutex); 8302be7d22fSVladimir Kondratiev rc = __wil_up(wil); 8312be7d22fSVladimir Kondratiev mutex_unlock(&wil->mutex); 8322be7d22fSVladimir Kondratiev 8332be7d22fSVladimir Kondratiev return rc; 8342be7d22fSVladimir Kondratiev } 8352be7d22fSVladimir Kondratiev 83673d839aeSVladimir Kondratiev int __wil_down(struct wil6210_priv *wil) 8372be7d22fSVladimir Kondratiev { 838f172b563SDedy Lansky int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS / 839f172b563SDedy Lansky WAIT_FOR_DISCONNECT_INTERVAL_MS; 840f172b563SDedy Lansky 841097638a0SVladimir Kondratiev WARN_ON(!mutex_is_locked(&wil->mutex)); 842097638a0SVladimir Kondratiev 843f772ebfbSVladimir Kondratiev if (wil->platform_ops.bus_request) 844f772ebfbSVladimir Kondratiev wil->platform_ops.bus_request(wil->platform_handle, 0); 845f772ebfbSVladimir Kondratiev 84673d839aeSVladimir Kondratiev wil_disable_irq(wil); 8479419b6a2SVladimir Kondratiev if (test_and_clear_bit(wil_status_napi_en, wil->status)) { 848e0287c4aSVladimir Kondratiev napi_disable(&wil->napi_rx); 849e0287c4aSVladimir Kondratiev napi_disable(&wil->napi_tx); 85073d839aeSVladimir Kondratiev wil_dbg_misc(wil, "NAPI disable\n"); 85173d839aeSVladimir Kondratiev } 85273d839aeSVladimir Kondratiev wil_enable_irq(wil); 853e0287c4aSVladimir Kondratiev 8542be7d22fSVladimir Kondratiev if (wil->scan_request) { 8552a91d7d0SVladimir Kondratiev wil_dbg_misc(wil, "Abort scan_request 0x%p\n", 8562a91d7d0SVladimir Kondratiev wil->scan_request); 857047e5d74SVladimir Kondratiev del_timer_sync(&wil->scan_timer); 8582be7d22fSVladimir Kondratiev cfg80211_scan_done(wil->scan_request, true); 8592be7d22fSVladimir Kondratiev wil->scan_request = NULL; 8602be7d22fSVladimir Kondratiev } 8612be7d22fSVladimir Kondratiev 8629419b6a2SVladimir Kondratiev if (test_bit(wil_status_fwconnected, wil->status) || 8639419b6a2SVladimir Kondratiev test_bit(wil_status_fwconnecting, wil->status)) 864f172b563SDedy Lansky wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); 865f172b563SDedy Lansky 866f172b563SDedy Lansky /* make sure wil is idle (not connected) */ 867f172b563SDedy Lansky mutex_unlock(&wil->mutex); 868f172b563SDedy Lansky while (iter--) { 8699419b6a2SVladimir Kondratiev int idle = !test_bit(wil_status_fwconnected, wil->status) && 8709419b6a2SVladimir Kondratiev !test_bit(wil_status_fwconnecting, wil->status); 871f172b563SDedy Lansky if (idle) 872f172b563SDedy Lansky break; 873f172b563SDedy Lansky msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); 874f172b563SDedy Lansky } 875f172b563SDedy Lansky mutex_lock(&wil->mutex); 876f172b563SDedy Lansky 877f172b563SDedy Lansky if (!iter) 878f172b563SDedy Lansky wil_err(wil, "timeout waiting for idle FW/HW\n"); 879f172b563SDedy Lansky 8802be7d22fSVladimir Kondratiev wil_rx_fini(wil); 8812be7d22fSVladimir Kondratiev 8822be7d22fSVladimir Kondratiev return 0; 8832be7d22fSVladimir Kondratiev } 8842be7d22fSVladimir Kondratiev 8852be7d22fSVladimir Kondratiev int wil_down(struct wil6210_priv *wil) 8862be7d22fSVladimir Kondratiev { 8872be7d22fSVladimir Kondratiev int rc; 8882be7d22fSVladimir Kondratiev 8899cf10d62SVladimir Kondratiev wil_dbg_misc(wil, "%s()\n", __func__); 8909cf10d62SVladimir Kondratiev 891c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 8922be7d22fSVladimir Kondratiev mutex_lock(&wil->mutex); 8932be7d22fSVladimir Kondratiev rc = __wil_down(wil); 8942be7d22fSVladimir Kondratiev mutex_unlock(&wil->mutex); 8952be7d22fSVladimir Kondratiev 8962be7d22fSVladimir Kondratiev return rc; 8972be7d22fSVladimir Kondratiev } 8983df2cd36SVladimir Kondratiev 8993df2cd36SVladimir Kondratiev int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) 9003df2cd36SVladimir Kondratiev { 9013df2cd36SVladimir Kondratiev int i; 9023df2cd36SVladimir Kondratiev int rc = -ENOENT; 9033df2cd36SVladimir Kondratiev 9043df2cd36SVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { 9053df2cd36SVladimir Kondratiev if ((wil->sta[i].status != wil_sta_unused) && 906108d1eb6SVladimir Kondratiev ether_addr_equal(wil->sta[i].addr, mac)) { 9073df2cd36SVladimir Kondratiev rc = i; 9083df2cd36SVladimir Kondratiev break; 9093df2cd36SVladimir Kondratiev } 9103df2cd36SVladimir Kondratiev } 9113df2cd36SVladimir Kondratiev 9123df2cd36SVladimir Kondratiev return rc; 9133df2cd36SVladimir Kondratiev } 914