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