Commit | Line | Data |
---|---|---|
2be7d22f | 1 | /* |
1f80af2e | 2 | * Copyright (c) 2012-2015 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) |
4821e6d8 | 158 | wmi_disconnect_sta(wil, sta->addr, reason_code); |
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(); | |
100106d7 VK |
198 | wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, |
199 | reason_code, from_event ? "+" : "-"); | |
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; | |
261 | ||
7743882d | 262 | wil_dbg_misc(wil, "Connect timeout\n"); |
2be7d22f VK |
263 | |
264 | /* reschedule to thread context - disconnect won't | |
265 | * run from atomic context | |
266 | */ | |
267 | schedule_work(&wil->disconnect_worker); | |
268 | } | |
269 | ||
047e5d74 VK |
270 | static void wil_scan_timer_fn(ulong x) |
271 | { | |
272 | struct wil6210_priv *wil = (void *)x; | |
273 | ||
9419b6a2 | 274 | clear_bit(wil_status_fwready, wil->status); |
047e5d74 | 275 | wil_err(wil, "Scan timeout detected, start fw error recovery\n"); |
8ad6600f | 276 | wil_fw_error_recovery(wil); |
047e5d74 VK |
277 | } |
278 | ||
c33407a8 VK |
279 | static int wil_wait_for_recovery(struct wil6210_priv *wil) |
280 | { | |
281 | if (wait_event_interruptible(wil->wq, wil->recovery_state != | |
282 | fw_recovery_pending)) { | |
283 | wil_err(wil, "Interrupt, canceling recovery\n"); | |
284 | return -ERESTARTSYS; | |
285 | } | |
286 | if (wil->recovery_state != fw_recovery_running) { | |
287 | wil_info(wil, "Recovery cancelled\n"); | |
288 | return -EINTR; | |
289 | } | |
290 | wil_info(wil, "Proceed with recovery\n"); | |
291 | return 0; | |
292 | } | |
293 | ||
294 | void wil_set_recovery_state(struct wil6210_priv *wil, int state) | |
295 | { | |
296 | wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__, | |
297 | wil->recovery_state, state); | |
298 | ||
299 | wil->recovery_state = state; | |
300 | wake_up_interruptible(&wil->wq); | |
301 | } | |
302 | ||
ed6f9dc6 VK |
303 | static void wil_fw_error_worker(struct work_struct *work) |
304 | { | |
c33407a8 VK |
305 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, |
306 | fw_error_worker); | |
ed6f9dc6 VK |
307 | struct wireless_dev *wdev = wil->wdev; |
308 | ||
309 | wil_dbg_misc(wil, "fw error worker\n"); | |
310 | ||
cded9369 VK |
311 | if (!netif_running(wil_to_ndev(wil))) { |
312 | wil_info(wil, "No recovery - interface is down\n"); | |
313 | return; | |
314 | } | |
315 | ||
fc219eed VK |
316 | /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO |
317 | * passed since last recovery attempt | |
318 | */ | |
319 | if (time_is_after_jiffies(wil->last_fw_recovery + | |
320 | WIL6210_FW_RECOVERY_TO)) | |
321 | wil->recovery_count++; | |
322 | else | |
323 | wil->recovery_count = 1; /* fw was alive for a long time */ | |
324 | ||
325 | if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { | |
326 | wil_err(wil, "too many recovery attempts (%d), giving up\n", | |
327 | wil->recovery_count); | |
328 | return; | |
329 | } | |
330 | ||
331 | wil->last_fw_recovery = jiffies; | |
332 | ||
9c3bde56 | 333 | mutex_lock(&wil->mutex); |
ed6f9dc6 VK |
334 | switch (wdev->iftype) { |
335 | case NL80211_IFTYPE_STATION: | |
336 | case NL80211_IFTYPE_P2P_CLIENT: | |
337 | case NL80211_IFTYPE_MONITOR: | |
c33407a8 | 338 | wil_info(wil, "fw error recovery requested (try %d)...\n", |
fc219eed | 339 | wil->recovery_count); |
c33407a8 VK |
340 | if (!no_fw_recovery) |
341 | wil->recovery_state = fw_recovery_running; | |
342 | if (0 != wil_wait_for_recovery(wil)) | |
343 | break; | |
344 | ||
73d839ae VK |
345 | __wil_down(wil); |
346 | __wil_up(wil); | |
ed6f9dc6 VK |
347 | break; |
348 | case NL80211_IFTYPE_AP: | |
349 | case NL80211_IFTYPE_P2P_GO: | |
e240537b | 350 | wil_info(wil, "No recovery for AP-like interface\n"); |
ed6f9dc6 VK |
351 | /* recovery in these modes is done by upper layers */ |
352 | break; | |
353 | default: | |
e240537b VK |
354 | wil_err(wil, "No recovery - unknown interface type %d\n", |
355 | wdev->iftype); | |
ed6f9dc6 VK |
356 | break; |
357 | } | |
9c3bde56 | 358 | mutex_unlock(&wil->mutex); |
ed6f9dc6 VK |
359 | } |
360 | ||
9a177384 VK |
361 | static int wil_find_free_vring(struct wil6210_priv *wil) |
362 | { | |
363 | int i; | |
8fe59627 | 364 | |
9a177384 VK |
365 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { |
366 | if (!wil->vring_tx[i].va) | |
367 | return i; | |
368 | } | |
369 | return -EINVAL; | |
370 | } | |
371 | ||
41d6b093 VK |
372 | int wil_bcast_init(struct wil6210_priv *wil) |
373 | { | |
374 | int ri = wil->bcast_vring, rc; | |
375 | ||
376 | if ((ri >= 0) && wil->vring_tx[ri].va) | |
377 | return 0; | |
378 | ||
379 | ri = wil_find_free_vring(wil); | |
380 | if (ri < 0) | |
381 | return ri; | |
382 | ||
230d8442 | 383 | wil->bcast_vring = ri; |
41d6b093 | 384 | rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order); |
230d8442 VK |
385 | if (rc) |
386 | wil->bcast_vring = -1; | |
41d6b093 VK |
387 | |
388 | return rc; | |
389 | } | |
390 | ||
391 | void wil_bcast_fini(struct wil6210_priv *wil) | |
392 | { | |
393 | int ri = wil->bcast_vring; | |
394 | ||
395 | if (ri < 0) | |
396 | return; | |
397 | ||
398 | wil->bcast_vring = -1; | |
399 | wil_vring_fini_tx(wil, ri); | |
400 | } | |
401 | ||
d81079f1 VK |
402 | static void wil_connect_worker(struct work_struct *work) |
403 | { | |
9b1ba7b2 | 404 | int rc, cid, ringid; |
d81079f1 VK |
405 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, |
406 | connect_worker); | |
c5e96c91 DL |
407 | struct net_device *ndev = wil_to_ndev(wil); |
408 | ||
9b1ba7b2 | 409 | mutex_lock(&wil->mutex); |
d81079f1 | 410 | |
9b1ba7b2 | 411 | cid = wil->pending_connect_cid; |
d81079f1 VK |
412 | if (cid < 0) { |
413 | wil_err(wil, "No connection pending\n"); | |
9b1ba7b2 VK |
414 | goto out; |
415 | } | |
416 | ringid = wil_find_free_vring(wil); | |
417 | if (ringid < 0) { | |
418 | wil_err(wil, "No free vring found\n"); | |
419 | goto out; | |
d81079f1 VK |
420 | } |
421 | ||
9b1ba7b2 VK |
422 | wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", |
423 | cid, ringid); | |
d81079f1 | 424 | |
d3762b40 | 425 | rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); |
d81079f1 | 426 | wil->pending_connect_cid = -1; |
3df2cd36 VK |
427 | if (rc == 0) { |
428 | wil->sta[cid].status = wil_sta_connected; | |
c5e96c91 | 429 | netif_tx_wake_all_queues(ndev); |
3df2cd36 | 430 | } else { |
5140a5fd | 431 | wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true); |
3df2cd36 | 432 | } |
9b1ba7b2 VK |
433 | out: |
434 | mutex_unlock(&wil->mutex); | |
d81079f1 VK |
435 | } |
436 | ||
2be7d22f VK |
437 | int wil_priv_init(struct wil6210_priv *wil) |
438 | { | |
ec81b5ad DL |
439 | uint i; |
440 | ||
7743882d | 441 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 442 | |
3df2cd36 | 443 | memset(wil->sta, 0, sizeof(wil->sta)); |
ec81b5ad DL |
444 | for (i = 0; i < WIL6210_MAX_CID; i++) |
445 | spin_lock_init(&wil->sta[i].tid_rx_lock); | |
3df2cd36 | 446 | |
2be7d22f VK |
447 | mutex_init(&wil->mutex); |
448 | mutex_init(&wil->wmi_mutex); | |
3277213f | 449 | mutex_init(&wil->back_rx_mutex); |
3a124ed6 | 450 | mutex_init(&wil->back_tx_mutex); |
40822a90 | 451 | mutex_init(&wil->probe_client_mutex); |
2be7d22f VK |
452 | |
453 | init_completion(&wil->wmi_ready); | |
59502647 | 454 | init_completion(&wil->wmi_call); |
2be7d22f VK |
455 | |
456 | wil->pending_connect_cid = -1; | |
41d6b093 | 457 | wil->bcast_vring = -1; |
2be7d22f | 458 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); |
047e5d74 | 459 | setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); |
2be7d22f | 460 | |
d81079f1 | 461 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
2be7d22f VK |
462 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
463 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
ed6f9dc6 | 464 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); |
3277213f | 465 | INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker); |
3a124ed6 | 466 | INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker); |
40822a90 | 467 | INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker); |
2be7d22f VK |
468 | |
469 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
3277213f | 470 | INIT_LIST_HEAD(&wil->back_rx_pending); |
3a124ed6 | 471 | INIT_LIST_HEAD(&wil->back_tx_pending); |
40822a90 | 472 | INIT_LIST_HEAD(&wil->probe_client_pending); |
2be7d22f | 473 | spin_lock_init(&wil->wmi_ev_lock); |
c33407a8 | 474 | init_waitqueue_head(&wil->wq); |
2be7d22f | 475 | |
3277213f | 476 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); |
2be7d22f VK |
477 | if (!wil->wmi_wq) |
478 | return -EAGAIN; | |
479 | ||
3277213f VK |
480 | wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service"); |
481 | if (!wil->wq_service) | |
482 | goto out_wmi_wq; | |
2be7d22f | 483 | |
fc219eed | 484 | wil->last_fw_recovery = jiffies; |
1f80af2e VS |
485 | wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT; |
486 | wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT; | |
487 | wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT; | |
488 | wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT; | |
fc219eed | 489 | |
ab954628 VK |
490 | if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) |
491 | rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; | |
2be7d22f | 492 | return 0; |
3277213f VK |
493 | |
494 | out_wmi_wq: | |
495 | destroy_workqueue(wil->wmi_wq); | |
496 | ||
497 | return -EAGAIN; | |
2be7d22f VK |
498 | } |
499 | ||
b516fcc5 VK |
500 | /** |
501 | * wil6210_disconnect - disconnect one connection | |
502 | * @wil: driver context | |
503 | * @bssid: peer to disconnect, NULL to disconnect all | |
4821e6d8 | 504 | * @reason_code: Reason code for the Disassociation frame |
b516fcc5 VK |
505 | * @from_event: whether is invoked from FW event handler |
506 | * | |
507 | * Disconnect and release associated resources. If invoked not from the | |
508 | * FW event handler, issue WMI command(s) to trigger MAC disconnect. | |
509 | */ | |
510 | void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, | |
4821e6d8 | 511 | u16 reason_code, bool from_event) |
2be7d22f | 512 | { |
9cf10d62 VK |
513 | wil_dbg_misc(wil, "%s()\n", __func__); |
514 | ||
2be7d22f | 515 | del_timer_sync(&wil->connect_timer); |
4821e6d8 | 516 | _wil6210_disconnect(wil, bssid, reason_code, from_event); |
2be7d22f VK |
517 | } |
518 | ||
519 | void wil_priv_deinit(struct wil6210_priv *wil) | |
520 | { | |
9cf10d62 VK |
521 | wil_dbg_misc(wil, "%s()\n", __func__); |
522 | ||
c33407a8 | 523 | wil_set_recovery_state(wil, fw_recovery_idle); |
047e5d74 | 524 | del_timer_sync(&wil->scan_timer); |
2be7d22f | 525 | cancel_work_sync(&wil->disconnect_worker); |
ed6f9dc6 | 526 | cancel_work_sync(&wil->fw_error_worker); |
097638a0 | 527 | mutex_lock(&wil->mutex); |
4821e6d8 | 528 | wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); |
097638a0 | 529 | mutex_unlock(&wil->mutex); |
2be7d22f | 530 | wmi_event_flush(wil); |
3277213f VK |
531 | wil_back_rx_flush(wil); |
532 | cancel_work_sync(&wil->back_rx_worker); | |
3a124ed6 VK |
533 | wil_back_tx_flush(wil); |
534 | cancel_work_sync(&wil->back_tx_worker); | |
40822a90 VK |
535 | wil_probe_client_flush(wil); |
536 | cancel_work_sync(&wil->probe_client_worker); | |
3277213f | 537 | destroy_workqueue(wil->wq_service); |
2be7d22f VK |
538 | destroy_workqueue(wil->wmi_wq); |
539 | } | |
540 | ||
151a9706 VK |
541 | static inline void wil_halt_cpu(struct wil6210_priv *wil) |
542 | { | |
b9eeb512 VK |
543 | wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST); |
544 | wil_w(wil, RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST); | |
151a9706 VK |
545 | } |
546 | ||
547 | static inline void wil_release_cpu(struct wil6210_priv *wil) | |
548 | { | |
549 | /* Start CPU */ | |
b9eeb512 | 550 | wil_w(wil, RGF_USER_USER_CPU_0, 1); |
151a9706 VK |
551 | } |
552 | ||
bbb2adc7 | 553 | static int wil_target_reset(struct wil6210_priv *wil) |
2be7d22f | 554 | { |
98a65b59 | 555 | int delay = 0; |
bb6c8dcc | 556 | u32 x, x1 = 0; |
36b10a72 | 557 | |
1aeda13b | 558 | wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); |
6508281b VK |
559 | |
560 | /* Clear MAC link up */ | |
b9eeb512 VK |
561 | wil_s(wil, RGF_HP_CTRL, BIT(15)); |
562 | wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD); | |
563 | wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST); | |
151a9706 VK |
564 | |
565 | wil_halt_cpu(wil); | |
2be7d22f | 566 | |
2cd0f021 | 567 | /* clear all boot loader "ready" bits */ |
b9eeb512 VK |
568 | wil_w(wil, RGF_USER_BL + |
569 | offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0); | |
cce47711 | 570 | /* Clear Fw Download notification */ |
b9eeb512 | 571 | wil_c(wil, RGF_USER_USAGE_6, BIT(0)); |
cce47711 | 572 | |
b9eeb512 | 573 | wil_s(wil, RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN); |
9a5511b5 VK |
574 | /* XTAL stabilization should take about 3ms */ |
575 | usleep_range(5000, 7000); | |
b9eeb512 | 576 | x = wil_r(wil, RGF_CAF_PLL_LOCK_STATUS); |
9a5511b5 VK |
577 | if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) { |
578 | wil_err(wil, "Xtal stabilization timeout\n" | |
579 | "RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x); | |
580 | return -ETIME; | |
6508281b | 581 | } |
9a5511b5 | 582 | /* switch 10k to XTAL*/ |
b9eeb512 | 583 | wil_c(wil, RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF); |
9a5511b5 | 584 | /* 40 MHz */ |
b9eeb512 | 585 | wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); |
9a5511b5 | 586 | |
b9eeb512 VK |
587 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); |
588 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf); | |
6508281b | 589 | |
b9eeb512 VK |
590 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
591 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
592 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x000000f0); | |
593 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00); | |
2be7d22f | 594 | |
b9eeb512 VK |
595 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); |
596 | wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0); | |
6508281b | 597 | |
b9eeb512 VK |
598 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
599 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
600 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
601 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
2be7d22f | 602 | |
b9eeb512 VK |
603 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); |
604 | /* reset A2 PCIE AHB */ | |
605 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); | |
6508281b | 606 | |
b9eeb512 | 607 | wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
2be7d22f | 608 | |
2cd0f021 | 609 | /* wait until device ready. typical time is 20..80 msec */ |
36b10a72 | 610 | do { |
520d68e7 | 611 | msleep(RST_DELAY); |
b9eeb512 VK |
612 | x = wil_r(wil, RGF_USER_BL + |
613 | offsetof(struct bl_dedicated_registers_v0, | |
614 | boot_loader_ready)); | |
bb6c8dcc VK |
615 | if (x1 != x) { |
616 | wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); | |
617 | x1 = x; | |
618 | } | |
520d68e7 | 619 | if (delay++ > RST_COUNT) { |
2cd0f021 | 620 | wil_err(wil, "Reset not completed, bl.ready 0x%08x\n", |
48516298 | 621 | x); |
bbb2adc7 | 622 | return -ETIME; |
36b10a72 | 623 | } |
f1ad8c93 | 624 | } while (x != BL_READY); |
36b10a72 | 625 | |
b9eeb512 | 626 | wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); |
972072aa | 627 | |
e3351277 | 628 | /* enable fix for HW bug related to the SA/DA swap in AP Rx */ |
b9eeb512 VK |
629 | wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | |
630 | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); | |
e3351277 | 631 | |
520d68e7 | 632 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); |
bbb2adc7 | 633 | return 0; |
151a9706 | 634 | } |
2be7d22f | 635 | |
2be7d22f VK |
636 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) |
637 | { | |
638 | le32_to_cpus(&r->base); | |
639 | le16_to_cpus(&r->entry_size); | |
640 | le16_to_cpus(&r->size); | |
641 | le32_to_cpus(&r->tail); | |
642 | le32_to_cpus(&r->head); | |
643 | } | |
644 | ||
2cd0f021 VK |
645 | static int wil_get_bl_info(struct wil6210_priv *wil) |
646 | { | |
647 | struct net_device *ndev = wil_to_ndev(wil); | |
f1ad8c93 VK |
648 | union { |
649 | struct bl_dedicated_registers_v0 bl0; | |
650 | struct bl_dedicated_registers_v1 bl1; | |
651 | } bl; | |
652 | u32 bl_ver; | |
653 | u8 *mac; | |
654 | u16 rf_status; | |
655 | ||
19c871ce VK |
656 | wil_memcpy_fromio_32(&bl, wil->csr + HOSTADDR(RGF_USER_BL), |
657 | sizeof(bl)); | |
658 | bl_ver = le32_to_cpu(bl.bl0.boot_loader_struct_version); | |
659 | mac = bl.bl0.mac_address; | |
660 | ||
661 | if (bl_ver == 0) { | |
f1ad8c93 VK |
662 | le32_to_cpus(&bl.bl0.rf_type); |
663 | le32_to_cpus(&bl.bl0.baseband_type); | |
f1ad8c93 VK |
664 | rf_status = 0; /* actually, unknown */ |
665 | wil_info(wil, | |
666 | "Boot Loader struct v%d: MAC = %pM RF = 0x%08x bband = 0x%08x\n", | |
667 | bl_ver, mac, | |
668 | bl.bl0.rf_type, bl.bl0.baseband_type); | |
669 | wil_info(wil, "Boot Loader build unknown for struct v0\n"); | |
19c871ce | 670 | } else { |
f1ad8c93 VK |
671 | le16_to_cpus(&bl.bl1.rf_type); |
672 | rf_status = le16_to_cpu(bl.bl1.rf_status); | |
673 | le32_to_cpus(&bl.bl1.baseband_type); | |
674 | le16_to_cpus(&bl.bl1.bl_version_subminor); | |
675 | le16_to_cpus(&bl.bl1.bl_version_build); | |
f1ad8c93 VK |
676 | wil_info(wil, |
677 | "Boot Loader struct v%d: MAC = %pM RF = 0x%04x (status 0x%04x) bband = 0x%08x\n", | |
678 | bl_ver, mac, | |
679 | bl.bl1.rf_type, rf_status, | |
680 | bl.bl1.baseband_type); | |
681 | wil_info(wil, "Boot Loader build %d.%d.%d.%d\n", | |
682 | bl.bl1.bl_version_major, bl.bl1.bl_version_minor, | |
683 | bl.bl1.bl_version_subminor, bl.bl1.bl_version_build); | |
f1ad8c93 | 684 | } |
2cd0f021 | 685 | |
f1ad8c93 VK |
686 | if (!is_valid_ether_addr(mac)) { |
687 | wil_err(wil, "BL: Invalid MAC %pM\n", mac); | |
2cd0f021 VK |
688 | return -EINVAL; |
689 | } | |
690 | ||
f1ad8c93 | 691 | ether_addr_copy(ndev->perm_addr, mac); |
2cd0f021 | 692 | if (!is_valid_ether_addr(ndev->dev_addr)) |
f1ad8c93 VK |
693 | ether_addr_copy(ndev->dev_addr, mac); |
694 | ||
695 | if (rf_status) {/* bad RF cable? */ | |
696 | wil_err(wil, "RF communication error 0x%04x", | |
697 | rf_status); | |
698 | return -EAGAIN; | |
699 | } | |
2cd0f021 VK |
700 | |
701 | return 0; | |
702 | } | |
703 | ||
409ead54 VK |
704 | static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err) |
705 | { | |
706 | u32 bl_assert_code, bl_assert_blink, bl_magic_number; | |
707 | u32 bl_ver = wil_r(wil, RGF_USER_BL + | |
708 | offsetof(struct bl_dedicated_registers_v0, | |
709 | boot_loader_struct_version)); | |
710 | ||
711 | if (bl_ver < 2) | |
712 | return; | |
713 | ||
714 | bl_assert_code = wil_r(wil, RGF_USER_BL + | |
715 | offsetof(struct bl_dedicated_registers_v1, | |
716 | bl_assert_code)); | |
717 | bl_assert_blink = wil_r(wil, RGF_USER_BL + | |
718 | offsetof(struct bl_dedicated_registers_v1, | |
719 | bl_assert_blink)); | |
720 | bl_magic_number = wil_r(wil, RGF_USER_BL + | |
721 | offsetof(struct bl_dedicated_registers_v1, | |
722 | bl_magic_number)); | |
723 | ||
724 | if (is_err) { | |
725 | wil_err(wil, | |
726 | "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n", | |
727 | bl_assert_code, bl_assert_blink, bl_magic_number); | |
728 | } else { | |
729 | wil_dbg_misc(wil, | |
730 | "BL assert code 0x%08x blink 0x%08x magic 0x%08x\n", | |
731 | bl_assert_code, bl_assert_blink, bl_magic_number); | |
732 | } | |
733 | } | |
734 | ||
2be7d22f VK |
735 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) |
736 | { | |
737 | ulong to = msecs_to_jiffies(1000); | |
738 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
8fe59627 | 739 | |
2be7d22f VK |
740 | if (0 == left) { |
741 | wil_err(wil, "Firmware not ready\n"); | |
742 | return -ETIME; | |
743 | } else { | |
15e23124 VK |
744 | wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", |
745 | jiffies_to_msecs(to-left), wil->hw_version); | |
2be7d22f VK |
746 | } |
747 | return 0; | |
748 | } | |
749 | ||
750 | /* | |
751 | * We reset all the structures, and we reset the UMAC. | |
752 | * After calling this routine, you're expected to reload | |
753 | * the firmware. | |
754 | */ | |
2cd0f021 | 755 | int wil_reset(struct wil6210_priv *wil, bool load_fw) |
2be7d22f VK |
756 | { |
757 | int rc; | |
758 | ||
9cf10d62 VK |
759 | wil_dbg_misc(wil, "%s()\n", __func__); |
760 | ||
097638a0 | 761 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
9419b6a2 | 762 | WARN_ON(test_bit(wil_status_napi_en, wil->status)); |
097638a0 | 763 | |
bfc2dc7a VK |
764 | if (debug_fw) { |
765 | static const u8 mac[ETH_ALEN] = { | |
766 | 0x00, 0xde, 0xad, 0x12, 0x34, 0x56, | |
767 | }; | |
768 | struct net_device *ndev = wil_to_ndev(wil); | |
769 | ||
770 | ether_addr_copy(ndev->perm_addr, mac); | |
771 | ether_addr_copy(ndev->dev_addr, ndev->perm_addr); | |
772 | return 0; | |
773 | } | |
774 | ||
67131a1d VK |
775 | if (wil->hw_version == HW_VER_UNKNOWN) |
776 | return -ENODEV; | |
777 | ||
f13e0630 HK |
778 | set_bit(wil_status_resetting, wil->status); |
779 | ||
097638a0 | 780 | cancel_work_sync(&wil->disconnect_worker); |
4821e6d8 | 781 | wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); |
41d6b093 | 782 | wil_bcast_fini(wil); |
097638a0 | 783 | |
452133a7 ME |
784 | /* prevent NAPI from being scheduled and prevent wmi commands */ |
785 | mutex_lock(&wil->wmi_mutex); | |
9419b6a2 | 786 | bitmap_zero(wil->status, wil_status_last); |
452133a7 | 787 | mutex_unlock(&wil->wmi_mutex); |
0fef1818 | 788 | |
ed6f9dc6 VK |
789 | if (wil->scan_request) { |
790 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", | |
791 | wil->scan_request); | |
047e5d74 | 792 | del_timer_sync(&wil->scan_timer); |
ed6f9dc6 VK |
793 | cfg80211_scan_done(wil->scan_request, true); |
794 | wil->scan_request = NULL; | |
795 | } | |
796 | ||
e4dbb093 | 797 | wil_mask_irq(wil); |
e08b5906 | 798 | |
2be7d22f VK |
799 | wmi_event_flush(wil); |
800 | ||
3277213f | 801 | flush_workqueue(wil->wq_service); |
e08b5906 | 802 | flush_workqueue(wil->wmi_wq); |
2be7d22f | 803 | |
409ead54 | 804 | wil_bl_crash_info(wil, false); |
bbb2adc7 | 805 | rc = wil_target_reset(wil); |
8bf6adb9 | 806 | wil_rx_fini(wil); |
409ead54 VK |
807 | if (rc) { |
808 | wil_bl_crash_info(wil, true); | |
bbb2adc7 | 809 | return rc; |
409ead54 | 810 | } |
bbb2adc7 | 811 | |
2cd0f021 | 812 | rc = wil_get_bl_info(wil); |
f1ad8c93 VK |
813 | if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */ |
814 | rc = 0; | |
2cd0f021 VK |
815 | if (rc) |
816 | return rc; | |
817 | ||
818 | if (load_fw) { | |
819 | wil_info(wil, "Use firmware <%s> + board <%s>\n", WIL_FW_NAME, | |
820 | WIL_FW2_NAME); | |
821 | ||
151a9706 VK |
822 | wil_halt_cpu(wil); |
823 | /* Loading f/w from the file */ | |
824 | rc = wil_request_firmware(wil, WIL_FW_NAME); | |
825 | if (rc) | |
826 | return rc; | |
2cd0f021 VK |
827 | rc = wil_request_firmware(wil, WIL_FW2_NAME); |
828 | if (rc) | |
829 | return rc; | |
830 | ||
831 | /* Mark FW as loaded from host */ | |
b9eeb512 | 832 | wil_s(wil, RGF_USER_USAGE_6, 1); |
151a9706 | 833 | |
2cd0f021 VK |
834 | /* clear any interrupts which on-card-firmware |
835 | * may have set | |
836 | */ | |
151a9706 | 837 | wil6210_clear_irq(wil); |
2cd0f021 VK |
838 | /* CAF_ICR - clear and mask */ |
839 | /* it is W1C, clear by writing back same value */ | |
b9eeb512 VK |
840 | wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0); |
841 | wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0); | |
2cd0f021 | 842 | |
151a9706 | 843 | wil_release_cpu(wil); |
151a9706 | 844 | } |
8bf6adb9 | 845 | |
2be7d22f VK |
846 | /* init after reset */ |
847 | wil->pending_connect_cid = -1; | |
02beaf1a | 848 | wil->ap_isolate = 0; |
16735d02 | 849 | reinit_completion(&wil->wmi_ready); |
59502647 | 850 | reinit_completion(&wil->wmi_call); |
2be7d22f | 851 | |
2cd0f021 VK |
852 | if (load_fw) { |
853 | wil_configure_interrupt_moderation(wil); | |
854 | wil_unmask_irq(wil); | |
2be7d22f | 855 | |
2cd0f021 VK |
856 | /* we just started MAC, wait for FW ready */ |
857 | rc = wil_wait_for_fw_ready(wil); | |
cec94d8c VK |
858 | if (rc == 0) /* check FW is responsive */ |
859 | rc = wmi_echo(wil); | |
2cd0f021 | 860 | } |
2be7d22f VK |
861 | |
862 | return rc; | |
863 | } | |
864 | ||
ed6f9dc6 VK |
865 | void wil_fw_error_recovery(struct wil6210_priv *wil) |
866 | { | |
867 | wil_dbg_misc(wil, "starting fw error recovery\n"); | |
f13e0630 HK |
868 | |
869 | if (test_bit(wil_status_resetting, wil->status)) { | |
870 | wil_info(wil, "Reset already in progress\n"); | |
871 | return; | |
872 | } | |
873 | ||
c33407a8 | 874 | wil->recovery_state = fw_recovery_pending; |
ed6f9dc6 VK |
875 | schedule_work(&wil->fw_error_worker); |
876 | } | |
2be7d22f | 877 | |
73d839ae | 878 | int __wil_up(struct wil6210_priv *wil) |
2be7d22f VK |
879 | { |
880 | struct net_device *ndev = wil_to_ndev(wil); | |
881 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 882 | int rc; |
2be7d22f | 883 | |
097638a0 VK |
884 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
885 | ||
2cd0f021 | 886 | rc = wil_reset(wil, true); |
2be7d22f VK |
887 | if (rc) |
888 | return rc; | |
889 | ||
e31b2562 | 890 | /* Rx VRING. After MAC and beacon */ |
d3762b40 | 891 | rc = wil_rx_init(wil, 1 << rx_ring_order); |
e31b2562 VK |
892 | if (rc) |
893 | return rc; | |
894 | ||
2be7d22f VK |
895 | switch (wdev->iftype) { |
896 | case NL80211_IFTYPE_STATION: | |
7743882d | 897 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
898 | ndev->type = ARPHRD_ETHER; |
899 | break; | |
900 | case NL80211_IFTYPE_AP: | |
7743882d | 901 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
902 | ndev->type = ARPHRD_ETHER; |
903 | break; | |
904 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 905 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
906 | ndev->type = ARPHRD_ETHER; |
907 | break; | |
908 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 909 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
910 | ndev->type = ARPHRD_ETHER; |
911 | break; | |
912 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 913 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
914 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
915 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
916 | break; | |
917 | default: | |
918 | return -EOPNOTSUPP; | |
919 | } | |
920 | ||
2be7d22f VK |
921 | /* MAC address - pre-requisite for other commands */ |
922 | wmi_set_mac_address(wil, ndev->dev_addr); | |
923 | ||
73d839ae | 924 | wil_dbg_misc(wil, "NAPI enable\n"); |
e0287c4a VK |
925 | napi_enable(&wil->napi_rx); |
926 | napi_enable(&wil->napi_tx); | |
9419b6a2 | 927 | set_bit(wil_status_napi_en, wil->status); |
e0287c4a | 928 | |
f772ebfb VK |
929 | if (wil->platform_ops.bus_request) |
930 | wil->platform_ops.bus_request(wil->platform_handle, | |
931 | WIL_MAX_BUS_REQUEST_KBPS); | |
932 | ||
2be7d22f VK |
933 | return 0; |
934 | } | |
935 | ||
936 | int wil_up(struct wil6210_priv *wil) | |
937 | { | |
938 | int rc; | |
939 | ||
9cf10d62 VK |
940 | wil_dbg_misc(wil, "%s()\n", __func__); |
941 | ||
2be7d22f VK |
942 | mutex_lock(&wil->mutex); |
943 | rc = __wil_up(wil); | |
944 | mutex_unlock(&wil->mutex); | |
945 | ||
946 | return rc; | |
947 | } | |
948 | ||
73d839ae | 949 | int __wil_down(struct wil6210_priv *wil) |
2be7d22f | 950 | { |
f172b563 DL |
951 | int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS / |
952 | WAIT_FOR_DISCONNECT_INTERVAL_MS; | |
953 | ||
097638a0 VK |
954 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
955 | ||
f772ebfb VK |
956 | if (wil->platform_ops.bus_request) |
957 | wil->platform_ops.bus_request(wil->platform_handle, 0); | |
958 | ||
73d839ae | 959 | wil_disable_irq(wil); |
9419b6a2 | 960 | if (test_and_clear_bit(wil_status_napi_en, wil->status)) { |
73d839ae VK |
961 | napi_disable(&wil->napi_rx); |
962 | napi_disable(&wil->napi_tx); | |
963 | wil_dbg_misc(wil, "NAPI disable\n"); | |
964 | } | |
965 | wil_enable_irq(wil); | |
e0287c4a | 966 | |
2be7d22f | 967 | if (wil->scan_request) { |
2a91d7d0 VK |
968 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", |
969 | wil->scan_request); | |
047e5d74 | 970 | del_timer_sync(&wil->scan_timer); |
2be7d22f VK |
971 | cfg80211_scan_done(wil->scan_request, true); |
972 | wil->scan_request = NULL; | |
973 | } | |
974 | ||
9419b6a2 VK |
975 | if (test_bit(wil_status_fwconnected, wil->status) || |
976 | test_bit(wil_status_fwconnecting, wil->status)) | |
f172b563 DL |
977 | wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); |
978 | ||
979 | /* make sure wil is idle (not connected) */ | |
980 | mutex_unlock(&wil->mutex); | |
981 | while (iter--) { | |
9419b6a2 VK |
982 | int idle = !test_bit(wil_status_fwconnected, wil->status) && |
983 | !test_bit(wil_status_fwconnecting, wil->status); | |
f172b563 DL |
984 | if (idle) |
985 | break; | |
986 | msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); | |
987 | } | |
988 | mutex_lock(&wil->mutex); | |
989 | ||
990 | if (!iter) | |
991 | wil_err(wil, "timeout waiting for idle FW/HW\n"); | |
992 | ||
2cd0f021 | 993 | wil_reset(wil, false); |
2be7d22f VK |
994 | |
995 | return 0; | |
996 | } | |
997 | ||
998 | int wil_down(struct wil6210_priv *wil) | |
999 | { | |
1000 | int rc; | |
1001 | ||
9cf10d62 VK |
1002 | wil_dbg_misc(wil, "%s()\n", __func__); |
1003 | ||
c33407a8 | 1004 | wil_set_recovery_state(wil, fw_recovery_idle); |
2be7d22f VK |
1005 | mutex_lock(&wil->mutex); |
1006 | rc = __wil_down(wil); | |
1007 | mutex_unlock(&wil->mutex); | |
1008 | ||
1009 | return rc; | |
1010 | } | |
3df2cd36 VK |
1011 | |
1012 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
1013 | { | |
1014 | int i; | |
1015 | int rc = -ENOENT; | |
1016 | ||
1017 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
1018 | if ((wil->sta[i].status != wil_sta_unused) && | |
108d1eb6 | 1019 | ether_addr_equal(wil->sta[i].addr, mac)) { |
3df2cd36 VK |
1020 | rc = i; |
1021 | break; | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | return rc; | |
1026 | } |