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