Commit | Line | Data |
---|---|---|
2be7d22f | 1 | /* |
0916d9f2 | 2 | * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. |
2be7d22f VK |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
2be7d22f VK |
17 | #include <linux/moduleparam.h> |
18 | #include <linux/if_arp.h> | |
108d1eb6 | 19 | #include <linux/etherdevice.h> |
2be7d22f VK |
20 | |
21 | #include "wil6210.h" | |
b4490f42 | 22 | #include "txrx.h" |
f172b563 | 23 | #include "wmi.h" |
f1ad8c93 | 24 | #include "boot_loader.h" |
f172b563 DL |
25 | |
26 | #define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000 | |
27 | #define WAIT_FOR_DISCONNECT_INTERVAL_MS 10 | |
2be7d22f | 28 | |
bfc2dc7a VK |
29 | bool debug_fw; /* = false; */ |
30 | module_param(debug_fw, bool, S_IRUGO); | |
31 | MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug"); | |
32 | ||
c33407a8 | 33 | bool no_fw_recovery; |
ed6f9dc6 | 34 | module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); |
c33407a8 | 35 | MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery"); |
ed6f9dc6 | 36 | |
ab954628 VK |
37 | /* if not set via modparam, will be set to default value of 1/8 of |
38 | * rx ring size during init flow | |
39 | */ | |
40 | unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT; | |
41 | module_param(rx_ring_overflow_thrsh, ushort, S_IRUGO); | |
42 | MODULE_PARM_DESC(rx_ring_overflow_thrsh, | |
43 | " RX ring overflow threshold in descriptors."); | |
b6b1b0ec | 44 | |
9a06bec9 VK |
45 | /* We allow allocation of more than 1 page buffers to support large packets. |
46 | * It is suboptimal behavior performance wise in case MTU above page size. | |
47 | */ | |
c44690a1 | 48 | unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; |
9a06bec9 VK |
49 | static int mtu_max_set(const char *val, const struct kernel_param *kp) |
50 | { | |
51 | int ret; | |
52 | ||
53 | /* sets mtu_max directly. no need to restore it in case of | |
54 | * illegal value since we assume this will fail insmod | |
55 | */ | |
56 | ret = param_set_uint(val, kp); | |
57 | if (ret) | |
58 | return ret; | |
59 | ||
4590d812 | 60 | if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU) |
9a06bec9 VK |
61 | ret = -EINVAL; |
62 | ||
63 | return ret; | |
64 | } | |
65 | ||
9c27847d | 66 | static const struct kernel_param_ops mtu_max_ops = { |
9a06bec9 VK |
67 | .set = mtu_max_set, |
68 | .get = param_get_uint, | |
69 | }; | |
70 | ||
71 | module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO); | |
72 | MODULE_PARM_DESC(mtu_max, " Max MTU value."); | |
73 | ||
d3762b40 VK |
74 | static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT; |
75 | static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT; | |
41d6b093 | 76 | static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT; |
d3762b40 VK |
77 | |
78 | static int ring_order_set(const char *val, const struct kernel_param *kp) | |
79 | { | |
80 | int ret; | |
81 | uint x; | |
82 | ||
83 | ret = kstrtouint(val, 0, &x); | |
84 | if (ret) | |
85 | return ret; | |
86 | ||
87 | if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX)) | |
88 | return -EINVAL; | |
89 | ||
90 | *((uint *)kp->arg) = x; | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
9c27847d | 95 | static const struct kernel_param_ops ring_order_ops = { |
d3762b40 VK |
96 | .set = ring_order_set, |
97 | .get = param_get_uint, | |
98 | }; | |
99 | ||
100 | module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO); | |
101 | MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order"); | |
102 | module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO); | |
103 | MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order"); | |
d507d1b7 VK |
104 | module_param_cb(bcast_ring_order, &ring_order_ops, &bcast_ring_order, S_IRUGO); |
105 | MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order"); | |
d3762b40 | 106 | |
520d68e7 VK |
107 | #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */ |
108 | #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */ | |
109 | ||
2be7d22f VK |
110 | /* |
111 | * Due to a hardware issue, | |
112 | * one has to read/write to/from NIC in 32-bit chunks; | |
113 | * regular memcpy_fromio and siblings will | |
114 | * not work on 64-bit platform - it uses 64-bit transactions | |
115 | * | |
116 | * Force 32-bit transactions to enable NIC on 64-bit platforms | |
117 | * | |
118 | * To avoid byte swap on big endian host, __raw_{read|write}l | |
119 | * should be used - {read|write}l would swap bytes to provide | |
120 | * little endian on PCI value in host endianness. | |
121 | */ | |
122 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | |
123 | size_t count) | |
124 | { | |
125 | u32 *d = dst; | |
126 | const volatile u32 __iomem *s = src; | |
127 | ||
128 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | |
129 | for (count += 4; count > 4; count -= 4) | |
130 | *d++ = __raw_readl(s++); | |
131 | } | |
132 | ||
133 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | |
134 | size_t count) | |
135 | { | |
136 | volatile u32 __iomem *d = dst; | |
137 | const u32 *s = src; | |
138 | ||
139 | for (count += 4; count > 4; count -= 4) | |
140 | __raw_writel(*s++, d++); | |
141 | } | |
142 | ||
b516fcc5 | 143 | static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, |
4821e6d8 | 144 | u16 reason_code, bool from_event) |
bd33273b | 145 | __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) |
91886b0b VK |
146 | { |
147 | uint i; | |
fc58f681 VK |
148 | struct net_device *ndev = wil_to_ndev(wil); |
149 | struct wireless_dev *wdev = wil->wdev; | |
91886b0b | 150 | struct wil_sta_info *sta = &wil->sta[cid]; |
8fe59627 | 151 | |
bd33273b | 152 | might_sleep(); |
fc58f681 VK |
153 | wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, |
154 | sta->status); | |
4d55a0a1 VK |
155 | |
156 | if (sta->status != wil_sta_unused) { | |
b516fcc5 | 157 | if (!from_event) |
0916d9f2 | 158 | wmi_disconnect_sta(wil, sta->addr, reason_code, true); |
b516fcc5 | 159 | |
fc58f681 VK |
160 | switch (wdev->iftype) { |
161 | case NL80211_IFTYPE_AP: | |
162 | case NL80211_IFTYPE_P2P_GO: | |
163 | /* AP-like interface */ | |
164 | cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); | |
165 | break; | |
166 | default: | |
167 | break; | |
168 | } | |
4d55a0a1 VK |
169 | sta->status = wil_sta_unused; |
170 | } | |
171 | ||
91886b0b | 172 | for (i = 0; i < WIL_STA_TID_NUM; i++) { |
ec81b5ad | 173 | struct wil_tid_ampdu_rx *r; |
ec81b5ad | 174 | |
bd33273b | 175 | spin_lock_bh(&sta->tid_rx_lock); |
ec81b5ad DL |
176 | |
177 | r = sta->tid_rx[i]; | |
91886b0b VK |
178 | sta->tid_rx[i] = NULL; |
179 | wil_tid_ampdu_rx_free(wil, r); | |
ec81b5ad | 180 | |
bd33273b | 181 | spin_unlock_bh(&sta->tid_rx_lock); |
91886b0b VK |
182 | } |
183 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | |
184 | if (wil->vring2cid_tid[i][0] == cid) | |
185 | wil_vring_fini_tx(wil, i); | |
186 | } | |
187 | memset(&sta->stats, 0, sizeof(sta->stats)); | |
188 | } | |
189 | ||
b516fcc5 | 190 | static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, |
4821e6d8 | 191 | u16 reason_code, bool from_event) |
2be7d22f | 192 | { |
91886b0b | 193 | int cid = -ENOENT; |
2be7d22f | 194 | struct net_device *ndev = wil_to_ndev(wil); |
91886b0b VK |
195 | struct wireless_dev *wdev = wil->wdev; |
196 | ||
197 | might_sleep(); | |
0916d9f2 ME |
198 | wil_info(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, |
199 | reason_code, from_event ? "+" : "-"); | |
100106d7 VK |
200 | |
201 | /* Cases are: | |
202 | * - disconnect single STA, still connected | |
203 | * - disconnect single STA, already disconnected | |
204 | * - disconnect all | |
205 | * | |
68682b41 | 206 | * For "disconnect all", there are 3 options: |
100106d7 | 207 | * - bssid == NULL |
68682b41 | 208 | * - bssid is broadcast address (ff:ff:ff:ff:ff:ff) |
100106d7 VK |
209 | * - bssid is our MAC address |
210 | */ | |
68682b41 VK |
211 | if (bssid && !is_broadcast_ether_addr(bssid) && |
212 | !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) { | |
91886b0b | 213 | cid = wil_find_cid(wil, bssid); |
100106d7 VK |
214 | wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", |
215 | bssid, cid, reason_code); | |
216 | if (cid >= 0) /* disconnect 1 peer */ | |
217 | wil_disconnect_cid(wil, cid, reason_code, from_event); | |
218 | } else { /* all */ | |
219 | wil_dbg_misc(wil, "Disconnect all\n"); | |
91886b0b | 220 | for (cid = 0; cid < WIL6210_MAX_CID; cid++) |
4821e6d8 | 221 | wil_disconnect_cid(wil, cid, reason_code, from_event); |
100106d7 | 222 | } |
91886b0b VK |
223 | |
224 | /* link state */ | |
225 | switch (wdev->iftype) { | |
226 | case NL80211_IFTYPE_STATION: | |
227 | case NL80211_IFTYPE_P2P_CLIENT: | |
41d6b093 | 228 | wil_bcast_fini(wil); |
c5e96c91 DL |
229 | netif_tx_stop_all_queues(ndev); |
230 | netif_carrier_off(ndev); | |
231 | ||
9419b6a2 VK |
232 | if (test_bit(wil_status_fwconnected, wil->status)) { |
233 | clear_bit(wil_status_fwconnected, wil->status); | |
4821e6d8 | 234 | cfg80211_disconnected(ndev, reason_code, |
80279fb7 | 235 | NULL, 0, false, GFP_KERNEL); |
9419b6a2 | 236 | } else if (test_bit(wil_status_fwconnecting, wil->status)) { |
91886b0b VK |
237 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, |
238 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
239 | GFP_KERNEL); | |
240 | } | |
9419b6a2 | 241 | clear_bit(wil_status_fwconnecting, wil->status); |
91886b0b VK |
242 | break; |
243 | default: | |
91886b0b | 244 | break; |
2be7d22f | 245 | } |
2be7d22f VK |
246 | } |
247 | ||
248 | static void wil_disconnect_worker(struct work_struct *work) | |
249 | { | |
250 | struct wil6210_priv *wil = container_of(work, | |
251 | struct wil6210_priv, disconnect_worker); | |
252 | ||
097638a0 | 253 | mutex_lock(&wil->mutex); |
4821e6d8 | 254 | _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false); |
097638a0 | 255 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
256 | } |
257 | ||
258 | static void wil_connect_timer_fn(ulong x) | |
259 | { | |
260 | struct wil6210_priv *wil = (void *)x; | |
0916d9f2 | 261 | bool q; |
2be7d22f | 262 | |
0916d9f2 | 263 | wil_err(wil, "Connect timeout detected, disconnect station\n"); |
2be7d22f VK |
264 | |
265 | /* reschedule to thread context - disconnect won't | |
0916d9f2 ME |
266 | * run from atomic context. |
267 | * queue on wmi_wq to prevent race with connect event. | |
2be7d22f | 268 | */ |
0916d9f2 ME |
269 | q = queue_work(wil->wmi_wq, &wil->disconnect_worker); |
270 | wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q); | |
2be7d22f VK |
271 | } |
272 | ||
047e5d74 VK |
273 | static void wil_scan_timer_fn(ulong x) |
274 | { | |
275 | struct wil6210_priv *wil = (void *)x; | |
276 | ||
9419b6a2 | 277 | clear_bit(wil_status_fwready, wil->status); |
047e5d74 | 278 | wil_err(wil, "Scan timeout detected, start fw error recovery\n"); |
8ad6600f | 279 | wil_fw_error_recovery(wil); |
047e5d74 VK |
280 | } |
281 | ||
c33407a8 VK |
282 | static int wil_wait_for_recovery(struct wil6210_priv *wil) |
283 | { | |
284 | if (wait_event_interruptible(wil->wq, wil->recovery_state != | |
285 | fw_recovery_pending)) { | |
286 | wil_err(wil, "Interrupt, canceling recovery\n"); | |
287 | return -ERESTARTSYS; | |
288 | } | |
289 | if (wil->recovery_state != fw_recovery_running) { | |
290 | wil_info(wil, "Recovery cancelled\n"); | |
291 | return -EINTR; | |
292 | } | |
293 | wil_info(wil, "Proceed with recovery\n"); | |
294 | return 0; | |
295 | } | |
296 | ||
297 | void wil_set_recovery_state(struct wil6210_priv *wil, int state) | |
298 | { | |
299 | wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__, | |
300 | wil->recovery_state, state); | |
301 | ||
302 | wil->recovery_state = state; | |
303 | wake_up_interruptible(&wil->wq); | |
304 | } | |
305 | ||
ed6f9dc6 VK |
306 | static void wil_fw_error_worker(struct work_struct *work) |
307 | { | |
c33407a8 VK |
308 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, |
309 | fw_error_worker); | |
ed6f9dc6 VK |
310 | struct wireless_dev *wdev = wil->wdev; |
311 | ||
312 | wil_dbg_misc(wil, "fw error worker\n"); | |
313 | ||
cded9369 VK |
314 | if (!netif_running(wil_to_ndev(wil))) { |
315 | wil_info(wil, "No recovery - interface is down\n"); | |
316 | return; | |
317 | } | |
318 | ||
fc219eed VK |
319 | /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO |
320 | * passed since last recovery attempt | |
321 | */ | |
322 | if (time_is_after_jiffies(wil->last_fw_recovery + | |
323 | WIL6210_FW_RECOVERY_TO)) | |
324 | wil->recovery_count++; | |
325 | else | |
326 | wil->recovery_count = 1; /* fw was alive for a long time */ | |
327 | ||
328 | if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { | |
329 | wil_err(wil, "too many recovery attempts (%d), giving up\n", | |
330 | wil->recovery_count); | |
331 | return; | |
332 | } | |
333 | ||
334 | wil->last_fw_recovery = jiffies; | |
335 | ||
9c3bde56 | 336 | mutex_lock(&wil->mutex); |
ed6f9dc6 VK |
337 | switch (wdev->iftype) { |
338 | case NL80211_IFTYPE_STATION: | |
339 | case NL80211_IFTYPE_P2P_CLIENT: | |
340 | case NL80211_IFTYPE_MONITOR: | |
c33407a8 | 341 | wil_info(wil, "fw error recovery requested (try %d)...\n", |
fc219eed | 342 | wil->recovery_count); |
c33407a8 VK |
343 | if (!no_fw_recovery) |
344 | wil->recovery_state = fw_recovery_running; | |
345 | if (0 != wil_wait_for_recovery(wil)) | |
346 | break; | |
347 | ||
73d839ae VK |
348 | __wil_down(wil); |
349 | __wil_up(wil); | |
ed6f9dc6 VK |
350 | break; |
351 | case NL80211_IFTYPE_AP: | |
352 | case NL80211_IFTYPE_P2P_GO: | |
e240537b | 353 | wil_info(wil, "No recovery for AP-like interface\n"); |
ed6f9dc6 VK |
354 | /* recovery in these modes is done by upper layers */ |
355 | break; | |
356 | default: | |
e240537b VK |
357 | wil_err(wil, "No recovery - unknown interface type %d\n", |
358 | wdev->iftype); | |
ed6f9dc6 VK |
359 | break; |
360 | } | |
9c3bde56 | 361 | mutex_unlock(&wil->mutex); |
ed6f9dc6 VK |
362 | } |
363 | ||
9a177384 VK |
364 | static int wil_find_free_vring(struct wil6210_priv *wil) |
365 | { | |
366 | int i; | |
8fe59627 | 367 | |
9a177384 VK |
368 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { |
369 | if (!wil->vring_tx[i].va) | |
370 | return i; | |
371 | } | |
372 | return -EINVAL; | |
373 | } | |
374 | ||
0916d9f2 ME |
375 | int wil_tx_init(struct wil6210_priv *wil, int cid) |
376 | { | |
377 | int rc = -EINVAL, ringid; | |
378 | ||
379 | if (cid < 0) { | |
380 | wil_err(wil, "No connection pending\n"); | |
381 | goto out; | |
382 | } | |
383 | ringid = wil_find_free_vring(wil); | |
384 | if (ringid < 0) { | |
385 | wil_err(wil, "No free vring found\n"); | |
386 | goto out; | |
387 | } | |
388 | ||
389 | wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", | |
390 | cid, ringid); | |
391 | ||
392 | rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); | |
393 | if (rc) | |
394 | wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n", | |
395 | cid, ringid); | |
396 | ||
397 | out: | |
398 | return rc; | |
399 | } | |
400 | ||
41d6b093 VK |
401 | int wil_bcast_init(struct wil6210_priv *wil) |
402 | { | |
403 | int ri = wil->bcast_vring, rc; | |
404 | ||
405 | if ((ri >= 0) && wil->vring_tx[ri].va) | |
406 | return 0; | |
407 | ||
408 | ri = wil_find_free_vring(wil); | |
409 | if (ri < 0) | |
410 | return ri; | |
411 | ||
230d8442 | 412 | wil->bcast_vring = ri; |
41d6b093 | 413 | rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order); |
230d8442 VK |
414 | if (rc) |
415 | wil->bcast_vring = -1; | |
41d6b093 VK |
416 | |
417 | return rc; | |
418 | } | |
419 | ||
420 | void wil_bcast_fini(struct wil6210_priv *wil) | |
421 | { | |
422 | int ri = wil->bcast_vring; | |
423 | ||
424 | if (ri < 0) | |
425 | return; | |
426 | ||
427 | wil->bcast_vring = -1; | |
428 | wil_vring_fini_tx(wil, ri); | |
429 | } | |
430 | ||
2be7d22f VK |
431 | int wil_priv_init(struct wil6210_priv *wil) |
432 | { | |
ec81b5ad DL |
433 | uint i; |
434 | ||
7743882d | 435 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 436 | |
3df2cd36 | 437 | memset(wil->sta, 0, sizeof(wil->sta)); |
ec81b5ad DL |
438 | for (i = 0; i < WIL6210_MAX_CID; i++) |
439 | spin_lock_init(&wil->sta[i].tid_rx_lock); | |
3df2cd36 | 440 | |
875e9439 ME |
441 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) |
442 | spin_lock_init(&wil->vring_tx_data[i].lock); | |
443 | ||
2be7d22f VK |
444 | mutex_init(&wil->mutex); |
445 | mutex_init(&wil->wmi_mutex); | |
3277213f | 446 | mutex_init(&wil->back_rx_mutex); |
3a124ed6 | 447 | mutex_init(&wil->back_tx_mutex); |
40822a90 | 448 | mutex_init(&wil->probe_client_mutex); |
2be7d22f VK |
449 | |
450 | init_completion(&wil->wmi_ready); | |
59502647 | 451 | init_completion(&wil->wmi_call); |
2be7d22f | 452 | |
41d6b093 | 453 | wil->bcast_vring = -1; |
2be7d22f | 454 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); |
047e5d74 | 455 | setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); |
2be7d22f | 456 | |
2be7d22f VK |
457 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
458 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
ed6f9dc6 | 459 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); |
3277213f | 460 | INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker); |
3a124ed6 | 461 | INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker); |
40822a90 | 462 | INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker); |
2be7d22f VK |
463 | |
464 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
3277213f | 465 | INIT_LIST_HEAD(&wil->back_rx_pending); |
3a124ed6 | 466 | INIT_LIST_HEAD(&wil->back_tx_pending); |
40822a90 | 467 | INIT_LIST_HEAD(&wil->probe_client_pending); |
2be7d22f | 468 | spin_lock_init(&wil->wmi_ev_lock); |
c33407a8 | 469 | init_waitqueue_head(&wil->wq); |
2be7d22f | 470 | |
3277213f | 471 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); |
2be7d22f VK |
472 | if (!wil->wmi_wq) |
473 | return -EAGAIN; | |
474 | ||
3277213f VK |
475 | wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service"); |
476 | if (!wil->wq_service) | |
477 | goto out_wmi_wq; | |
2be7d22f | 478 | |
fc219eed | 479 | wil->last_fw_recovery = jiffies; |
1f80af2e VS |
480 | wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT; |
481 | wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT; | |
482 | wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT; | |
483 | wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT; | |
fc219eed | 484 | |
ab954628 VK |
485 | if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) |
486 | rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; | |
2be7d22f | 487 | return 0; |
3277213f VK |
488 | |
489 | out_wmi_wq: | |
490 | destroy_workqueue(wil->wmi_wq); | |
491 | ||
492 | return -EAGAIN; | |
2be7d22f VK |
493 | } |
494 | ||
b516fcc5 VK |
495 | /** |
496 | * wil6210_disconnect - disconnect one connection | |
497 | * @wil: driver context | |
498 | * @bssid: peer to disconnect, NULL to disconnect all | |
4821e6d8 | 499 | * @reason_code: Reason code for the Disassociation frame |
b516fcc5 VK |
500 | * @from_event: whether is invoked from FW event handler |
501 | * | |
502 | * Disconnect and release associated resources. If invoked not from the | |
503 | * FW event handler, issue WMI command(s) to trigger MAC disconnect. | |
504 | */ | |
505 | void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, | |
4821e6d8 | 506 | u16 reason_code, bool from_event) |
2be7d22f | 507 | { |
9cf10d62 VK |
508 | wil_dbg_misc(wil, "%s()\n", __func__); |
509 | ||
2be7d22f | 510 | del_timer_sync(&wil->connect_timer); |
4821e6d8 | 511 | _wil6210_disconnect(wil, bssid, reason_code, from_event); |
2be7d22f VK |
512 | } |
513 | ||
514 | void wil_priv_deinit(struct wil6210_priv *wil) | |
515 | { | |
9cf10d62 VK |
516 | wil_dbg_misc(wil, "%s()\n", __func__); |
517 | ||
c33407a8 | 518 | wil_set_recovery_state(wil, fw_recovery_idle); |
047e5d74 | 519 | del_timer_sync(&wil->scan_timer); |
2be7d22f | 520 | cancel_work_sync(&wil->disconnect_worker); |
ed6f9dc6 | 521 | cancel_work_sync(&wil->fw_error_worker); |
097638a0 | 522 | mutex_lock(&wil->mutex); |
4821e6d8 | 523 | wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); |
097638a0 | 524 | mutex_unlock(&wil->mutex); |
2be7d22f | 525 | wmi_event_flush(wil); |
3277213f VK |
526 | wil_back_rx_flush(wil); |
527 | cancel_work_sync(&wil->back_rx_worker); | |
3a124ed6 VK |
528 | wil_back_tx_flush(wil); |
529 | cancel_work_sync(&wil->back_tx_worker); | |
40822a90 VK |
530 | wil_probe_client_flush(wil); |
531 | cancel_work_sync(&wil->probe_client_worker); | |
3277213f | 532 | destroy_workqueue(wil->wq_service); |
2be7d22f VK |
533 | destroy_workqueue(wil->wmi_wq); |
534 | } | |
535 | ||
151a9706 VK |
536 | static inline void wil_halt_cpu(struct wil6210_priv *wil) |
537 | { | |
b9eeb512 VK |
538 | wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); |
539 | wil_w(wil, RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST); | |
151a9706 VK |
540 | } |
541 | ||
542 | static inline void wil_release_cpu(struct wil6210_priv *wil) | |
543 | { | |
544 | /* Start CPU */ | |
b9eeb512 | 545 | wil_w(wil, RGF_USER_USER_CPU_0, 1); |
151a9706 VK |
546 | } |
547 | ||
bbb2adc7 | 548 | static int wil_target_reset(struct wil6210_priv *wil) |
2be7d22f | 549 | { |
98a65b59 | 550 | int delay = 0; |
bb6c8dcc | 551 | u32 x, x1 = 0; |
36b10a72 | 552 | |
1aeda13b | 553 | wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); |
6508281b VK |
554 | |
555 | /* Clear MAC link up */ | |
b9eeb512 VK |
556 | wil_s(wil, RGF_HP_CTRL, BIT(15)); |
557 | wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD); | |
558 | wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST); | |
151a9706 VK |
559 | |
560 | wil_halt_cpu(wil); | |
2be7d22f | 561 | |
2cd0f021 | 562 | /* clear all boot loader "ready" bits */ |
b9eeb512 VK |
563 | wil_w(wil, RGF_USER_BL + |
564 | offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); | |
cce47711 | 565 | /* Clear Fw Download notification */ |
b9eeb512 | 566 | wil_c(wil, RGF_USER_USAGE_6, BIT(0)); |
cce47711 | 567 | |
b9eeb512 | 568 | wil_s(wil, RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN); |
9a5511b5 VK |
569 | /* XTAL stabilization should take about 3ms */ |
570 | usleep_range(5000, 7000); | |
b9eeb512 | 571 | x = wil_r(wil, RGF_CAF_PLL_LOCK_STATUS); |
9a5511b5 VK |
572 | if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) { |
573 | wil_err(wil, "Xtal stabilization timeout\n" | |
574 | "RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x); | |
575 | return -ETIME; | |
6508281b | 576 | } |
9a5511b5 | 577 | /* switch 10k to XTAL*/ |
b9eeb512 | 578 | wil_c(wil, RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF); |
9a5511b5 | 579 | /* 40 MHz */ |
b9eeb512 | 580 | wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); |
9a5511b5 | 581 | |
b9eeb512 VK |
582 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); |
583 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf); | |
6508281b | 584 | |
b9eeb512 VK |
585 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
586 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
587 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x000000f0); | |
588 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00); | |
2be7d22f | 589 | |
b9eeb512 VK |
590 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); |
591 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0); | |
6508281b | 592 | |
b9eeb512 VK |
593 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
594 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
595 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
596 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
2be7d22f | 597 | |
b9eeb512 VK |
598 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); |
599 | /* reset A2 PCIE AHB */ | |
600 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); | |
6508281b | 601 | |
b9eeb512 | 602 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
2be7d22f | 603 | |
2cd0f021 | 604 | /* wait until device ready. typical time is 20..80 msec */ |
36b10a72 | 605 | do { |
520d68e7 | 606 | msleep(RST_DELAY); |
b9eeb512 VK |
607 | x = wil_r(wil, RGF_USER_BL + |
608 | offsetof(struct bl_dedicated_registers_v0, | |
609 | boot_loader_ready)); | |
bb6c8dcc VK |
610 | if (x1 != x) { |
611 | wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); | |
612 | x1 = x; | |
613 | } | |
520d68e7 | 614 | if (delay++ > RST_COUNT) { |
2cd0f021 | 615 | wil_err(wil, "Reset not completed, bl.ready 0x%08x\n", |
48516298 | 616 | x); |
bbb2adc7 | 617 | return -ETIME; |
36b10a72 | 618 | } |
f1ad8c93 | 619 | } while (x != BL_READY); |
36b10a72 | 620 | |
b9eeb512 | 621 | wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); |
972072aa | 622 | |
e3351277 | 623 | /* enable fix for HW bug related to the SA/DA swap in AP Rx */ |
b9eeb512 VK |
624 | wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | |
625 | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); | |
e3351277 | 626 | |
520d68e7 | 627 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); |
bbb2adc7 | 628 | return 0; |
151a9706 | 629 | } |
2be7d22f | 630 | |
2be7d22f VK |
631 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) |
632 | { | |
633 | le32_to_cpus(&r->base); | |
634 | le16_to_cpus(&r->entry_size); | |
635 | le16_to_cpus(&r->size); | |
636 | le32_to_cpus(&r->tail); | |
637 | le32_to_cpus(&r->head); | |
638 | } | |
639 | ||
2cd0f021 VK |
640 | static int wil_get_bl_info(struct wil6210_priv *wil) |
641 | { | |
642 | struct net_device *ndev = wil_to_ndev(wil); | |
f1ad8c93 VK |
643 | union { |
644 | struct bl_dedicated_registers_v0 bl0; | |
645 | struct bl_dedicated_registers_v1 bl1; | |
646 | } bl; | |
647 | u32 bl_ver; | |
648 | u8 *mac; | |
649 | u16 rf_status; | |
650 | ||
19c871ce VK |
651 | wil_memcpy_fromio_32(&bl, wil->csr + HOSTADDR(RGF_USER_BL), |
652 | sizeof(bl)); | |
653 | bl_ver = le32_to_cpu(bl.bl0.boot_loader_struct_version); | |
654 | mac = bl.bl0.mac_address; | |
655 | ||
656 | if (bl_ver == 0) { | |
f1ad8c93 VK |
657 | le32_to_cpus(&bl.bl0.rf_type); |
658 | le32_to_cpus(&bl.bl0.baseband_type); | |
f1ad8c93 VK |
659 | rf_status = 0; /* actually, unknown */ |
660 | wil_info(wil, | |
661 | "Boot Loader struct v%d: MAC = %pM RF = 0x%08x bband = 0x%08x\n", | |
662 | bl_ver, mac, | |
663 | bl.bl0.rf_type, bl.bl0.baseband_type); | |
664 | wil_info(wil, "Boot Loader build unknown for struct v0\n"); | |
19c871ce | 665 | } else { |
f1ad8c93 VK |
666 | le16_to_cpus(&bl.bl1.rf_type); |
667 | rf_status = le16_to_cpu(bl.bl1.rf_status); | |
668 | le32_to_cpus(&bl.bl1.baseband_type); | |
669 | le16_to_cpus(&bl.bl1.bl_version_subminor); | |
670 | le16_to_cpus(&bl.bl1.bl_version_build); | |
f1ad8c93 VK |
671 | wil_info(wil, |
672 | "Boot Loader struct v%d: MAC = %pM RF = 0x%04x (status 0x%04x) bband = 0x%08x\n", | |
673 | bl_ver, mac, | |
674 | bl.bl1.rf_type, rf_status, | |
675 | bl.bl1.baseband_type); | |
676 | wil_info(wil, "Boot Loader build %d.%d.%d.%d\n", | |
677 | bl.bl1.bl_version_major, bl.bl1.bl_version_minor, | |
678 | bl.bl1.bl_version_subminor, bl.bl1.bl_version_build); | |
f1ad8c93 | 679 | } |
2cd0f021 | 680 | |
f1ad8c93 VK |
681 | if (!is_valid_ether_addr(mac)) { |
682 | wil_err(wil, "BL: Invalid MAC %pM\n", mac); | |
2cd0f021 VK |
683 | return -EINVAL; |
684 | } | |
685 | ||
f1ad8c93 | 686 | ether_addr_copy(ndev->perm_addr, mac); |
2cd0f021 | 687 | if (!is_valid_ether_addr(ndev->dev_addr)) |
f1ad8c93 VK |
688 | ether_addr_copy(ndev->dev_addr, mac); |
689 | ||
690 | if (rf_status) {/* bad RF cable? */ | |
691 | wil_err(wil, "RF communication error 0x%04x", | |
692 | rf_status); | |
693 | return -EAGAIN; | |
694 | } | |
2cd0f021 VK |
695 | |
696 | return 0; | |
697 | } | |
698 | ||
409ead54 VK |
699 | static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err) |
700 | { | |
701 | u32 bl_assert_code, bl_assert_blink, bl_magic_number; | |
702 | u32 bl_ver = wil_r(wil, RGF_USER_BL + | |
703 | offsetof(struct bl_dedicated_registers_v0, | |
704 | boot_loader_struct_version)); | |
705 | ||
706 | if (bl_ver < 2) | |
707 | return; | |
708 | ||
709 | bl_assert_code = wil_r(wil, RGF_USER_BL + | |
710 | offsetof(struct bl_dedicated_registers_v1, | |
711 | bl_assert_code)); | |
712 | bl_assert_blink = wil_r(wil, RGF_USER_BL + | |
713 | offsetof(struct bl_dedicated_registers_v1, | |
714 | bl_assert_blink)); | |
715 | bl_magic_number = wil_r(wil, RGF_USER_BL + | |
716 | offsetof(struct bl_dedicated_registers_v1, | |
717 | bl_magic_number)); | |
718 | ||
719 | if (is_err) { | |
720 | wil_err(wil, | |
721 | "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n", | |
722 | bl_assert_code, bl_assert_blink, bl_magic_number); | |
723 | } else { | |
724 | wil_dbg_misc(wil, | |
725 | "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n", | |
726 | bl_assert_code, bl_assert_blink, bl_magic_number); | |
727 | } | |
728 | } | |
729 | ||
2be7d22f VK |
730 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) |
731 | { | |
732 | ulong to = msecs_to_jiffies(1000); | |
733 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
8fe59627 | 734 | |
2be7d22f VK |
735 | if (0 == left) { |
736 | wil_err(wil, "Firmware not ready\n"); | |
737 | return -ETIME; | |
738 | } else { | |
15e23124 VK |
739 | wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", |
740 | jiffies_to_msecs(to-left), wil->hw_version); | |
2be7d22f VK |
741 | } |
742 | return 0; | |
743 | } | |
744 | ||
745 | /* | |
746 | * We reset all the structures, and we reset the UMAC. | |
747 | * After calling this routine, you're expected to reload | |
748 | * the firmware. | |
749 | */ | |
2cd0f021 | 750 | int wil_reset(struct wil6210_priv *wil, bool load_fw) |
2be7d22f VK |
751 | { |
752 | int rc; | |
753 | ||
9cf10d62 VK |
754 | wil_dbg_misc(wil, "%s()\n", __func__); |
755 | ||
097638a0 | 756 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
9419b6a2 | 757 | WARN_ON(test_bit(wil_status_napi_en, wil->status)); |
097638a0 | 758 | |
bfc2dc7a VK |
759 | if (debug_fw) { |
760 | static const u8 mac[ETH_ALEN] = { | |
761 | 0x00, 0xde, 0xad, 0x12, 0x34, 0x56, | |
762 | }; | |
763 | struct net_device *ndev = wil_to_ndev(wil); | |
764 | ||
765 | ether_addr_copy(ndev->perm_addr, mac); | |
766 | ether_addr_copy(ndev->dev_addr, ndev->perm_addr); | |
767 | return 0; | |
768 | } | |
769 | ||
67131a1d VK |
770 | if (wil->hw_version == HW_VER_UNKNOWN) |
771 | return -ENODEV; | |
772 | ||
f13e0630 HK |
773 | set_bit(wil_status_resetting, wil->status); |
774 | ||
097638a0 | 775 | cancel_work_sync(&wil->disconnect_worker); |
4821e6d8 | 776 | wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); |
41d6b093 | 777 | wil_bcast_fini(wil); |
097638a0 | 778 | |
452133a7 ME |
779 | /* prevent NAPI from being scheduled and prevent wmi commands */ |
780 | mutex_lock(&wil->wmi_mutex); | |
9419b6a2 | 781 | bitmap_zero(wil->status, wil_status_last); |
452133a7 | 782 | mutex_unlock(&wil->wmi_mutex); |
0fef1818 | 783 | |
ed6f9dc6 VK |
784 | if (wil->scan_request) { |
785 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", | |
786 | wil->scan_request); | |
047e5d74 | 787 | del_timer_sync(&wil->scan_timer); |
ed6f9dc6 VK |
788 | cfg80211_scan_done(wil->scan_request, true); |
789 | wil->scan_request = NULL; | |
790 | } | |
791 | ||
e4dbb093 | 792 | wil_mask_irq(wil); |
e08b5906 | 793 | |
2be7d22f VK |
794 | wmi_event_flush(wil); |
795 | ||
3277213f | 796 | flush_workqueue(wil->wq_service); |
e08b5906 | 797 | flush_workqueue(wil->wmi_wq); |
2be7d22f | 798 | |
409ead54 | 799 | wil_bl_crash_info(wil, false); |
bbb2adc7 | 800 | rc = wil_target_reset(wil); |
8bf6adb9 | 801 | wil_rx_fini(wil); |
409ead54 VK |
802 | if (rc) { |
803 | wil_bl_crash_info(wil, true); | |
bbb2adc7 | 804 | return rc; |
409ead54 | 805 | } |
bbb2adc7 | 806 | |
2cd0f021 | 807 | rc = wil_get_bl_info(wil); |
f1ad8c93 VK |
808 | if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ |
809 | rc = 0; | |
2cd0f021 VK |
810 | if (rc) |
811 | return rc; | |
812 | ||
813 | if (load_fw) { | |
814 | wil_info(wil, "Use firmware <%s> + board <%s>\n", WIL_FW_NAME, | |
815 | WIL_FW2_NAME); | |
816 | ||
151a9706 VK |
817 | wil_halt_cpu(wil); |
818 | /* Loading f/w from the file */ | |
819 | rc = wil_request_firmware(wil, WIL_FW_NAME); | |
820 | if (rc) | |
821 | return rc; | |
2cd0f021 VK |
822 | rc = wil_request_firmware(wil, WIL_FW2_NAME); |
823 | if (rc) | |
824 | return rc; | |
825 | ||
826 | /* Mark FW as loaded from host */ | |
b9eeb512 | 827 | wil_s(wil, RGF_USER_USAGE_6, 1); |
151a9706 | 828 | |
2cd0f021 VK |
829 | /* clear any interrupts which on-card-firmware |
830 | * may have set | |
831 | */ | |
151a9706 | 832 | wil6210_clear_irq(wil); |
2cd0f021 VK |
833 | /* CAF_ICR - clear and mask */ |
834 | /* it is W1C, clear by writing back same value */ | |
b9eeb512 VK |
835 | wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); |
836 | wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); | |
2cd0f021 | 837 | |
151a9706 | 838 | wil_release_cpu(wil); |
151a9706 | 839 | } |
8bf6adb9 | 840 | |
2be7d22f | 841 | /* init after reset */ |
02beaf1a | 842 | wil->ap_isolate = 0; |
16735d02 | 843 | reinit_completion(&wil->wmi_ready); |
59502647 | 844 | reinit_completion(&wil->wmi_call); |
2be7d22f | 845 | |
2cd0f021 VK |
846 | if (load_fw) { |
847 | wil_configure_interrupt_moderation(wil); | |
848 | wil_unmask_irq(wil); | |
2be7d22f | 849 | |
2cd0f021 VK |
850 | /* we just started MAC, wait for FW ready */ |
851 | rc = wil_wait_for_fw_ready(wil); | |
cec94d8c VK |
852 | if (rc == 0) /* check FW is responsive */ |
853 | rc = wmi_echo(wil); | |
2cd0f021 | 854 | } |
2be7d22f VK |
855 | |
856 | return rc; | |
857 | } | |
858 | ||
ed6f9dc6 VK |
859 | void wil_fw_error_recovery(struct wil6210_priv *wil) |
860 | { | |
861 | wil_dbg_misc(wil, "starting fw error recovery\n"); | |
f13e0630 HK |
862 | |
863 | if (test_bit(wil_status_resetting, wil->status)) { | |
864 | wil_info(wil, "Reset already in progress\n"); | |
865 | return; | |
866 | } | |
867 | ||
c33407a8 | 868 | wil->recovery_state = fw_recovery_pending; |
ed6f9dc6 VK |
869 | schedule_work(&wil->fw_error_worker); |
870 | } | |
2be7d22f | 871 | |
73d839ae | 872 | int __wil_up(struct wil6210_priv *wil) |
2be7d22f VK |
873 | { |
874 | struct net_device *ndev = wil_to_ndev(wil); | |
875 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 876 | int rc; |
2be7d22f | 877 | |
097638a0 VK |
878 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
879 | ||
2cd0f021 | 880 | rc = wil_reset(wil, true); |
2be7d22f VK |
881 | if (rc) |
882 | return rc; | |
883 | ||
e31b2562 | 884 | /* Rx VRING. After MAC and beacon */ |
d3762b40 | 885 | rc = wil_rx_init(wil, 1 << rx_ring_order); |
e31b2562 VK |
886 | if (rc) |
887 | return rc; | |
888 | ||
2be7d22f VK |
889 | switch (wdev->iftype) { |
890 | case NL80211_IFTYPE_STATION: | |
7743882d | 891 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
892 | ndev->type = ARPHRD_ETHER; |
893 | break; | |
894 | case NL80211_IFTYPE_AP: | |
7743882d | 895 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
896 | ndev->type = ARPHRD_ETHER; |
897 | break; | |
898 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 899 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
900 | ndev->type = ARPHRD_ETHER; |
901 | break; | |
902 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 903 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
904 | ndev->type = ARPHRD_ETHER; |
905 | break; | |
906 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 907 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
908 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
909 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
910 | break; | |
911 | default: | |
912 | return -EOPNOTSUPP; | |
913 | } | |
914 | ||
2be7d22f VK |
915 | /* MAC address - pre-requisite for other commands */ |
916 | wmi_set_mac_address(wil, ndev->dev_addr); | |
917 | ||
73d839ae | 918 | wil_dbg_misc(wil, "NAPI enable\n"); |
e0287c4a VK |
919 | napi_enable(&wil->napi_rx); |
920 | napi_enable(&wil->napi_tx); | |
9419b6a2 | 921 | set_bit(wil_status_napi_en, wil->status); |
e0287c4a | 922 | |
f772ebfb VK |
923 | if (wil->platform_ops.bus_request) |
924 | wil->platform_ops.bus_request(wil->platform_handle, | |
925 | WIL_MAX_BUS_REQUEST_KBPS); | |
926 | ||
2be7d22f VK |
927 | return 0; |
928 | } | |
929 | ||
930 | int wil_up(struct wil6210_priv *wil) | |
931 | { | |
932 | int rc; | |
933 | ||
9cf10d62 VK |
934 | wil_dbg_misc(wil, "%s()\n", __func__); |
935 | ||
2be7d22f VK |
936 | mutex_lock(&wil->mutex); |
937 | rc = __wil_up(wil); | |
938 | mutex_unlock(&wil->mutex); | |
939 | ||
940 | return rc; | |
941 | } | |
942 | ||
73d839ae | 943 | int __wil_down(struct wil6210_priv *wil) |
2be7d22f | 944 | { |
f172b563 DL |
945 | int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS / |
946 | WAIT_FOR_DISCONNECT_INTERVAL_MS; | |
947 | ||
097638a0 VK |
948 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
949 | ||
f772ebfb VK |
950 | if (wil->platform_ops.bus_request) |
951 | wil->platform_ops.bus_request(wil->platform_handle, 0); | |
952 | ||
73d839ae | 953 | wil_disable_irq(wil); |
9419b6a2 | 954 | if (test_and_clear_bit(wil_status_napi_en, wil->status)) { |
73d839ae VK |
955 | napi_disable(&wil->napi_rx); |
956 | napi_disable(&wil->napi_tx); | |
957 | wil_dbg_misc(wil, "NAPI disable\n"); | |
958 | } | |
959 | wil_enable_irq(wil); | |
e0287c4a | 960 | |
2be7d22f | 961 | if (wil->scan_request) { |
2a91d7d0 VK |
962 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", |
963 | wil->scan_request); | |
047e5d74 | 964 | del_timer_sync(&wil->scan_timer); |
2be7d22f VK |
965 | cfg80211_scan_done(wil->scan_request, true); |
966 | wil->scan_request = NULL; | |
967 | } | |
968 | ||
9419b6a2 VK |
969 | if (test_bit(wil_status_fwconnected, wil->status) || |
970 | test_bit(wil_status_fwconnecting, wil->status)) | |
f172b563 DL |
971 | wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); |
972 | ||
973 | /* make sure wil is idle (not connected) */ | |
974 | mutex_unlock(&wil->mutex); | |
975 | while (iter--) { | |
9419b6a2 VK |
976 | int idle = !test_bit(wil_status_fwconnected, wil->status) && |
977 | !test_bit(wil_status_fwconnecting, wil->status); | |
f172b563 DL |
978 | if (idle) |
979 | break; | |
980 | msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); | |
981 | } | |
982 | mutex_lock(&wil->mutex); | |
983 | ||
f94c48d6 | 984 | if (iter < 0) |
f172b563 DL |
985 | wil_err(wil, "timeout waiting for idle FW/HW\n"); |
986 | ||
2cd0f021 | 987 | wil_reset(wil, false); |
2be7d22f VK |
988 | |
989 | return 0; | |
990 | } | |
991 | ||
992 | int wil_down(struct wil6210_priv *wil) | |
993 | { | |
994 | int rc; | |
995 | ||
9cf10d62 VK |
996 | wil_dbg_misc(wil, "%s()\n", __func__); |
997 | ||
c33407a8 | 998 | wil_set_recovery_state(wil, fw_recovery_idle); |
2be7d22f VK |
999 | mutex_lock(&wil->mutex); |
1000 | rc = __wil_down(wil); | |
1001 | mutex_unlock(&wil->mutex); | |
1002 | ||
1003 | return rc; | |
1004 | } | |
3df2cd36 VK |
1005 | |
1006 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
1007 | { | |
1008 | int i; | |
1009 | int rc = -ENOENT; | |
1010 | ||
1011 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
1012 | if ((wil->sta[i].status != wil_sta_unused) && | |
108d1eb6 | 1013 | ether_addr_equal(wil->sta[i].addr, mac)) { |
3df2cd36 VK |
1014 | rc = i; |
1015 | break; | |
1016 | } | |
1017 | } | |
1018 | ||
1019 | return rc; | |
1020 | } |