]>
Commit | Line | Data |
---|---|---|
2be7d22f VK |
1 | /* |
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | |
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" |
2be7d22f | 23 | |
ed6f9dc6 VK |
24 | static bool no_fw_recovery; |
25 | module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); | |
26 | MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery"); | |
27 | ||
2be7d22f VK |
28 | /* |
29 | * Due to a hardware issue, | |
30 | * one has to read/write to/from NIC in 32-bit chunks; | |
31 | * regular memcpy_fromio and siblings will | |
32 | * not work on 64-bit platform - it uses 64-bit transactions | |
33 | * | |
34 | * Force 32-bit transactions to enable NIC on 64-bit platforms | |
35 | * | |
36 | * To avoid byte swap on big endian host, __raw_{read|write}l | |
37 | * should be used - {read|write}l would swap bytes to provide | |
38 | * little endian on PCI value in host endianness. | |
39 | */ | |
40 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | |
41 | size_t count) | |
42 | { | |
43 | u32 *d = dst; | |
44 | const volatile u32 __iomem *s = src; | |
45 | ||
46 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | |
47 | for (count += 4; count > 4; count -= 4) | |
48 | *d++ = __raw_readl(s++); | |
49 | } | |
50 | ||
51 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | |
52 | size_t count) | |
53 | { | |
54 | volatile u32 __iomem *d = dst; | |
55 | const u32 *s = src; | |
56 | ||
57 | for (count += 4; count > 4; count -= 4) | |
58 | __raw_writel(*s++, d++); | |
59 | } | |
60 | ||
91886b0b VK |
61 | static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) |
62 | { | |
63 | uint i; | |
fc58f681 VK |
64 | struct net_device *ndev = wil_to_ndev(wil); |
65 | struct wireless_dev *wdev = wil->wdev; | |
91886b0b | 66 | struct wil_sta_info *sta = &wil->sta[cid]; |
fc58f681 VK |
67 | wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, |
68 | sta->status); | |
4d55a0a1 | 69 | |
e58c9f70 | 70 | sta->data_port_open = false; |
4d55a0a1 VK |
71 | if (sta->status != wil_sta_unused) { |
72 | wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); | |
fc58f681 VK |
73 | switch (wdev->iftype) { |
74 | case NL80211_IFTYPE_AP: | |
75 | case NL80211_IFTYPE_P2P_GO: | |
76 | /* AP-like interface */ | |
77 | cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); | |
78 | break; | |
79 | default: | |
80 | break; | |
81 | } | |
4d55a0a1 VK |
82 | sta->status = wil_sta_unused; |
83 | } | |
84 | ||
91886b0b VK |
85 | for (i = 0; i < WIL_STA_TID_NUM; i++) { |
86 | struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; | |
87 | sta->tid_rx[i] = NULL; | |
88 | wil_tid_ampdu_rx_free(wil, r); | |
89 | } | |
90 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | |
91 | if (wil->vring2cid_tid[i][0] == cid) | |
92 | wil_vring_fini_tx(wil, i); | |
93 | } | |
94 | memset(&sta->stats, 0, sizeof(sta->stats)); | |
95 | } | |
96 | ||
3b3a0162 | 97 | static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) |
2be7d22f | 98 | { |
91886b0b | 99 | int cid = -ENOENT; |
2be7d22f | 100 | struct net_device *ndev = wil_to_ndev(wil); |
91886b0b VK |
101 | struct wireless_dev *wdev = wil->wdev; |
102 | ||
103 | might_sleep(); | |
104 | if (bssid) { | |
105 | cid = wil_find_cid(wil, bssid); | |
106 | wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); | |
107 | } else { | |
108 | wil_dbg_misc(wil, "%s(all)\n", __func__); | |
b4490f42 VK |
109 | } |
110 | ||
91886b0b VK |
111 | if (cid >= 0) /* disconnect 1 peer */ |
112 | wil_disconnect_cid(wil, cid); | |
113 | else /* disconnect all */ | |
114 | for (cid = 0; cid < WIL6210_MAX_CID; cid++) | |
115 | wil_disconnect_cid(wil, cid); | |
116 | ||
117 | /* link state */ | |
118 | switch (wdev->iftype) { | |
119 | case NL80211_IFTYPE_STATION: | |
120 | case NL80211_IFTYPE_P2P_CLIENT: | |
121 | wil_link_off(wil); | |
122 | if (test_bit(wil_status_fwconnected, &wil->status)) { | |
123 | clear_bit(wil_status_fwconnected, &wil->status); | |
124 | cfg80211_disconnected(ndev, | |
125 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
126 | NULL, 0, GFP_KERNEL); | |
127 | } else if (test_bit(wil_status_fwconnecting, &wil->status)) { | |
128 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, | |
129 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
130 | GFP_KERNEL); | |
131 | } | |
132 | clear_bit(wil_status_fwconnecting, &wil->status); | |
91886b0b VK |
133 | break; |
134 | default: | |
91886b0b | 135 | break; |
2be7d22f | 136 | } |
2be7d22f VK |
137 | } |
138 | ||
139 | static void wil_disconnect_worker(struct work_struct *work) | |
140 | { | |
141 | struct wil6210_priv *wil = container_of(work, | |
142 | struct wil6210_priv, disconnect_worker); | |
143 | ||
097638a0 | 144 | mutex_lock(&wil->mutex); |
2be7d22f | 145 | _wil6210_disconnect(wil, NULL); |
097638a0 | 146 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
147 | } |
148 | ||
149 | static void wil_connect_timer_fn(ulong x) | |
150 | { | |
151 | struct wil6210_priv *wil = (void *)x; | |
152 | ||
7743882d | 153 | wil_dbg_misc(wil, "Connect timeout\n"); |
2be7d22f VK |
154 | |
155 | /* reschedule to thread context - disconnect won't | |
156 | * run from atomic context | |
157 | */ | |
158 | schedule_work(&wil->disconnect_worker); | |
159 | } | |
160 | ||
047e5d74 VK |
161 | static void wil_scan_timer_fn(ulong x) |
162 | { | |
163 | struct wil6210_priv *wil = (void *)x; | |
164 | ||
165 | clear_bit(wil_status_fwready, &wil->status); | |
166 | wil_err(wil, "Scan timeout detected, start fw error recovery\n"); | |
167 | schedule_work(&wil->fw_error_worker); | |
168 | } | |
169 | ||
ed6f9dc6 VK |
170 | static void wil_fw_error_worker(struct work_struct *work) |
171 | { | |
172 | struct wil6210_priv *wil = container_of(work, | |
173 | struct wil6210_priv, fw_error_worker); | |
174 | struct wireless_dev *wdev = wil->wdev; | |
175 | ||
176 | wil_dbg_misc(wil, "fw error worker\n"); | |
177 | ||
178 | if (no_fw_recovery) | |
179 | return; | |
180 | ||
fc219eed VK |
181 | /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO |
182 | * passed since last recovery attempt | |
183 | */ | |
184 | if (time_is_after_jiffies(wil->last_fw_recovery + | |
185 | WIL6210_FW_RECOVERY_TO)) | |
186 | wil->recovery_count++; | |
187 | else | |
188 | wil->recovery_count = 1; /* fw was alive for a long time */ | |
189 | ||
190 | if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { | |
191 | wil_err(wil, "too many recovery attempts (%d), giving up\n", | |
192 | wil->recovery_count); | |
193 | return; | |
194 | } | |
195 | ||
196 | wil->last_fw_recovery = jiffies; | |
197 | ||
9c3bde56 | 198 | mutex_lock(&wil->mutex); |
ed6f9dc6 VK |
199 | switch (wdev->iftype) { |
200 | case NL80211_IFTYPE_STATION: | |
201 | case NL80211_IFTYPE_P2P_CLIENT: | |
202 | case NL80211_IFTYPE_MONITOR: | |
fc219eed VK |
203 | wil_info(wil, "fw error recovery started (try %d)...\n", |
204 | wil->recovery_count); | |
ed6f9dc6 VK |
205 | wil_reset(wil); |
206 | ||
207 | /* need to re-allocate Rx ring after reset */ | |
208 | wil_rx_init(wil); | |
209 | break; | |
210 | case NL80211_IFTYPE_AP: | |
211 | case NL80211_IFTYPE_P2P_GO: | |
212 | /* recovery in these modes is done by upper layers */ | |
213 | break; | |
214 | default: | |
215 | break; | |
216 | } | |
9c3bde56 | 217 | mutex_unlock(&wil->mutex); |
ed6f9dc6 VK |
218 | } |
219 | ||
9a177384 VK |
220 | static int wil_find_free_vring(struct wil6210_priv *wil) |
221 | { | |
222 | int i; | |
223 | for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { | |
224 | if (!wil->vring_tx[i].va) | |
225 | return i; | |
226 | } | |
227 | return -EINVAL; | |
228 | } | |
229 | ||
d81079f1 VK |
230 | static void wil_connect_worker(struct work_struct *work) |
231 | { | |
232 | int rc; | |
233 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | |
234 | connect_worker); | |
235 | int cid = wil->pending_connect_cid; | |
9a177384 | 236 | int ringid = wil_find_free_vring(wil); |
d81079f1 VK |
237 | |
238 | if (cid < 0) { | |
239 | wil_err(wil, "No connection pending\n"); | |
240 | return; | |
241 | } | |
242 | ||
243 | wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); | |
244 | ||
9a177384 | 245 | rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); |
d81079f1 | 246 | wil->pending_connect_cid = -1; |
3df2cd36 VK |
247 | if (rc == 0) { |
248 | wil->sta[cid].status = wil_sta_connected; | |
d81079f1 | 249 | wil_link_on(wil); |
3df2cd36 VK |
250 | } else { |
251 | wil->sta[cid].status = wil_sta_unused; | |
252 | } | |
d81079f1 VK |
253 | } |
254 | ||
2be7d22f VK |
255 | int wil_priv_init(struct wil6210_priv *wil) |
256 | { | |
7743882d | 257 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f | 258 | |
3df2cd36 VK |
259 | memset(wil->sta, 0, sizeof(wil->sta)); |
260 | ||
2be7d22f VK |
261 | mutex_init(&wil->mutex); |
262 | mutex_init(&wil->wmi_mutex); | |
263 | ||
264 | init_completion(&wil->wmi_ready); | |
265 | ||
266 | wil->pending_connect_cid = -1; | |
267 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | |
047e5d74 | 268 | setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); |
2be7d22f | 269 | |
d81079f1 | 270 | INIT_WORK(&wil->connect_worker, wil_connect_worker); |
2be7d22f VK |
271 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); |
272 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | |
ed6f9dc6 | 273 | INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); |
2be7d22f VK |
274 | |
275 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | |
276 | spin_lock_init(&wil->wmi_ev_lock); | |
277 | ||
278 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | |
279 | if (!wil->wmi_wq) | |
280 | return -EAGAIN; | |
281 | ||
282 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | |
283 | if (!wil->wmi_wq_conn) { | |
284 | destroy_workqueue(wil->wmi_wq); | |
285 | return -EAGAIN; | |
286 | } | |
287 | ||
fc219eed VK |
288 | wil->last_fw_recovery = jiffies; |
289 | ||
2be7d22f VK |
290 | return 0; |
291 | } | |
292 | ||
3b3a0162 | 293 | void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) |
2be7d22f VK |
294 | { |
295 | del_timer_sync(&wil->connect_timer); | |
296 | _wil6210_disconnect(wil, bssid); | |
297 | } | |
298 | ||
299 | void wil_priv_deinit(struct wil6210_priv *wil) | |
300 | { | |
047e5d74 | 301 | del_timer_sync(&wil->scan_timer); |
2be7d22f | 302 | cancel_work_sync(&wil->disconnect_worker); |
ed6f9dc6 | 303 | cancel_work_sync(&wil->fw_error_worker); |
097638a0 | 304 | mutex_lock(&wil->mutex); |
2be7d22f | 305 | wil6210_disconnect(wil, NULL); |
097638a0 | 306 | mutex_unlock(&wil->mutex); |
2be7d22f VK |
307 | wmi_event_flush(wil); |
308 | destroy_workqueue(wil->wmi_wq_conn); | |
309 | destroy_workqueue(wil->wmi_wq); | |
310 | } | |
311 | ||
312 | static void wil_target_reset(struct wil6210_priv *wil) | |
313 | { | |
98a65b59 | 314 | int delay = 0; |
d28bcc30 | 315 | u32 hw_state; |
36b10a72 | 316 | u32 rev_id; |
6508281b | 317 | bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW); |
36b10a72 | 318 | |
6508281b | 319 | wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name); |
2be7d22f | 320 | |
36b10a72 VK |
321 | /* register read */ |
322 | #define R(a) ioread32(wil->csr + HOSTADDR(a)) | |
2be7d22f VK |
323 | /* register write */ |
324 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | |
325 | /* register set = read, OR, write */ | |
972072aa VK |
326 | #define S(a, v) W(a, R(a) | v) |
327 | /* register clear = read, AND with inverted, write */ | |
328 | #define C(a, v) W(a, R(a) & ~v) | |
2be7d22f | 329 | |
17123991 | 330 | wil->hw_version = R(RGF_USER_FW_REV_ID); |
36b10a72 | 331 | rev_id = wil->hw_version & 0xff; |
6508281b VK |
332 | |
333 | /* Clear MAC link up */ | |
334 | S(RGF_HP_CTRL, BIT(15)); | |
2be7d22f VK |
335 | /* hpal_perst_from_pad_src_n_mask */ |
336 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | |
337 | /* car_perst_rst_src_n_mask */ | |
338 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | |
260e6951 | 339 | wmb(); /* order is important here */ |
2be7d22f | 340 | |
6508281b VK |
341 | if (is_sparrow) { |
342 | W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); | |
343 | wmb(); /* order is important here */ | |
344 | } | |
345 | ||
2be7d22f VK |
346 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ |
347 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | |
260e6951 | 348 | wmb(); /* order is important here */ |
2be7d22f | 349 | |
2be7d22f VK |
350 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); |
351 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | |
6508281b | 352 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000B0 : 0x00000170); |
2be7d22f | 353 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); |
260e6951 | 354 | wmb(); /* order is important here */ |
2be7d22f | 355 | |
6508281b VK |
356 | if (is_sparrow) { |
357 | W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); | |
358 | wmb(); /* order is important here */ | |
359 | } | |
360 | ||
2be7d22f VK |
361 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); |
362 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | |
363 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | |
364 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | |
260e6951 | 365 | wmb(); /* order is important here */ |
2be7d22f | 366 | |
6508281b VK |
367 | if (is_sparrow) { |
368 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); | |
369 | /* reset A2 PCIE AHB */ | |
36b10a72 | 370 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); |
6508281b VK |
371 | |
372 | } else { | |
373 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | |
374 | if (rev_id == 1) { | |
375 | /* reset A1 BOTH PCIE AHB & PCIE RGF */ | |
376 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | |
377 | } else { | |
378 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); | |
379 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); | |
380 | } | |
381 | ||
36b10a72 | 382 | } |
6508281b VK |
383 | |
384 | /* TODO: check order here!!! Erez code is different */ | |
2be7d22f | 385 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); |
260e6951 | 386 | wmb(); /* order is important here */ |
2be7d22f | 387 | |
d28bcc30 | 388 | /* wait until device ready */ |
36b10a72 VK |
389 | do { |
390 | msleep(1); | |
d28bcc30 | 391 | hw_state = R(RGF_USER_HW_MACHINE_STATE); |
98a65b59 | 392 | if (delay++ > 100) { |
d28bcc30 VK |
393 | wil_err(wil, "Reset not completed, hw_state 0x%08x\n", |
394 | hw_state); | |
36b10a72 VK |
395 | return; |
396 | } | |
d28bcc30 | 397 | } while (hw_state != HW_MACHINE_BOOT_DONE); |
36b10a72 | 398 | |
6508281b VK |
399 | /* TODO: Erez check rev_id != 1 */ |
400 | if (!is_sparrow && (rev_id != 1)) | |
17123991 | 401 | W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); |
36b10a72 | 402 | |
972072aa | 403 | C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); |
260e6951 | 404 | wmb(); /* order is important here */ |
972072aa | 405 | |
98a65b59 | 406 | wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); |
2be7d22f | 407 | |
36b10a72 | 408 | #undef R |
2be7d22f VK |
409 | #undef W |
410 | #undef S | |
972072aa | 411 | #undef C |
2be7d22f VK |
412 | } |
413 | ||
414 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | |
415 | { | |
416 | le32_to_cpus(&r->base); | |
417 | le16_to_cpus(&r->entry_size); | |
418 | le16_to_cpus(&r->size); | |
419 | le32_to_cpus(&r->tail); | |
420 | le32_to_cpus(&r->head); | |
421 | } | |
422 | ||
423 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | |
424 | { | |
425 | ulong to = msecs_to_jiffies(1000); | |
426 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | |
427 | if (0 == left) { | |
428 | wil_err(wil, "Firmware not ready\n"); | |
429 | return -ETIME; | |
430 | } else { | |
15e23124 VK |
431 | wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", |
432 | jiffies_to_msecs(to-left), wil->hw_version); | |
2be7d22f VK |
433 | } |
434 | return 0; | |
435 | } | |
436 | ||
437 | /* | |
438 | * We reset all the structures, and we reset the UMAC. | |
439 | * After calling this routine, you're expected to reload | |
440 | * the firmware. | |
441 | */ | |
442 | int wil_reset(struct wil6210_priv *wil) | |
443 | { | |
444 | int rc; | |
445 | ||
097638a0 VK |
446 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
447 | ||
448 | cancel_work_sync(&wil->disconnect_worker); | |
449 | wil6210_disconnect(wil, NULL); | |
450 | ||
0fef1818 VK |
451 | wil->status = 0; /* prevent NAPI from being scheduled */ |
452 | if (test_bit(wil_status_napi_en, &wil->status)) { | |
453 | napi_synchronize(&wil->napi_rx); | |
0fef1818 VK |
454 | } |
455 | ||
ed6f9dc6 VK |
456 | if (wil->scan_request) { |
457 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", | |
458 | wil->scan_request); | |
047e5d74 | 459 | del_timer_sync(&wil->scan_timer); |
ed6f9dc6 VK |
460 | cfg80211_scan_done(wil->scan_request, true); |
461 | wil->scan_request = NULL; | |
462 | } | |
463 | ||
e08b5906 | 464 | wil6210_disable_irq(wil); |
e08b5906 | 465 | |
2be7d22f VK |
466 | wmi_event_flush(wil); |
467 | ||
2be7d22f | 468 | flush_workqueue(wil->wmi_wq_conn); |
e08b5906 | 469 | flush_workqueue(wil->wmi_wq); |
2be7d22f VK |
470 | |
471 | /* TODO: put MAC in reset */ | |
472 | wil_target_reset(wil); | |
473 | ||
8bf6adb9 VK |
474 | wil_rx_fini(wil); |
475 | ||
2be7d22f VK |
476 | /* init after reset */ |
477 | wil->pending_connect_cid = -1; | |
16735d02 | 478 | reinit_completion(&wil->wmi_ready); |
2be7d22f | 479 | |
2be7d22f VK |
480 | /* TODO: release MAC reset */ |
481 | wil6210_enable_irq(wil); | |
482 | ||
483 | /* we just started MAC, wait for FW ready */ | |
484 | rc = wil_wait_for_fw_ready(wil); | |
485 | ||
486 | return rc; | |
487 | } | |
488 | ||
ed6f9dc6 VK |
489 | void wil_fw_error_recovery(struct wil6210_priv *wil) |
490 | { | |
491 | wil_dbg_misc(wil, "starting fw error recovery\n"); | |
492 | schedule_work(&wil->fw_error_worker); | |
493 | } | |
2be7d22f VK |
494 | |
495 | void wil_link_on(struct wil6210_priv *wil) | |
496 | { | |
497 | struct net_device *ndev = wil_to_ndev(wil); | |
498 | ||
7743882d | 499 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
500 | |
501 | netif_carrier_on(ndev); | |
55f8f680 | 502 | wil_dbg_misc(wil, "netif_tx_wake : link on\n"); |
2be7d22f VK |
503 | netif_tx_wake_all_queues(ndev); |
504 | } | |
505 | ||
506 | void wil_link_off(struct wil6210_priv *wil) | |
507 | { | |
508 | struct net_device *ndev = wil_to_ndev(wil); | |
509 | ||
7743882d | 510 | wil_dbg_misc(wil, "%s()\n", __func__); |
2be7d22f VK |
511 | |
512 | netif_tx_stop_all_queues(ndev); | |
55f8f680 | 513 | wil_dbg_misc(wil, "netif_tx_stop : link off\n"); |
2be7d22f VK |
514 | netif_carrier_off(ndev); |
515 | } | |
516 | ||
517 | static int __wil_up(struct wil6210_priv *wil) | |
518 | { | |
519 | struct net_device *ndev = wil_to_ndev(wil); | |
520 | struct wireless_dev *wdev = wil->wdev; | |
2be7d22f | 521 | int rc; |
2be7d22f | 522 | |
097638a0 VK |
523 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
524 | ||
2be7d22f VK |
525 | rc = wil_reset(wil); |
526 | if (rc) | |
527 | return rc; | |
528 | ||
e31b2562 VK |
529 | /* Rx VRING. After MAC and beacon */ |
530 | rc = wil_rx_init(wil); | |
531 | if (rc) | |
532 | return rc; | |
533 | ||
2be7d22f VK |
534 | switch (wdev->iftype) { |
535 | case NL80211_IFTYPE_STATION: | |
7743882d | 536 | wil_dbg_misc(wil, "type: STATION\n"); |
2be7d22f VK |
537 | ndev->type = ARPHRD_ETHER; |
538 | break; | |
539 | case NL80211_IFTYPE_AP: | |
7743882d | 540 | wil_dbg_misc(wil, "type: AP\n"); |
2be7d22f VK |
541 | ndev->type = ARPHRD_ETHER; |
542 | break; | |
543 | case NL80211_IFTYPE_P2P_CLIENT: | |
7743882d | 544 | wil_dbg_misc(wil, "type: P2P_CLIENT\n"); |
2be7d22f VK |
545 | ndev->type = ARPHRD_ETHER; |
546 | break; | |
547 | case NL80211_IFTYPE_P2P_GO: | |
7743882d | 548 | wil_dbg_misc(wil, "type: P2P_GO\n"); |
2be7d22f VK |
549 | ndev->type = ARPHRD_ETHER; |
550 | break; | |
551 | case NL80211_IFTYPE_MONITOR: | |
7743882d | 552 | wil_dbg_misc(wil, "type: Monitor\n"); |
2be7d22f VK |
553 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; |
554 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | |
555 | break; | |
556 | default: | |
557 | return -EOPNOTSUPP; | |
558 | } | |
559 | ||
2be7d22f VK |
560 | /* MAC address - pre-requisite for other commands */ |
561 | wmi_set_mac_address(wil, ndev->dev_addr); | |
562 | ||
2be7d22f | 563 | |
e0287c4a VK |
564 | napi_enable(&wil->napi_rx); |
565 | napi_enable(&wil->napi_tx); | |
0fef1818 | 566 | set_bit(wil_status_napi_en, &wil->status); |
e0287c4a | 567 | |
2be7d22f VK |
568 | return 0; |
569 | } | |
570 | ||
571 | int wil_up(struct wil6210_priv *wil) | |
572 | { | |
573 | int rc; | |
574 | ||
575 | mutex_lock(&wil->mutex); | |
576 | rc = __wil_up(wil); | |
577 | mutex_unlock(&wil->mutex); | |
578 | ||
579 | return rc; | |
580 | } | |
581 | ||
582 | static int __wil_down(struct wil6210_priv *wil) | |
583 | { | |
097638a0 VK |
584 | WARN_ON(!mutex_is_locked(&wil->mutex)); |
585 | ||
0fef1818 | 586 | clear_bit(wil_status_napi_en, &wil->status); |
e0287c4a VK |
587 | napi_disable(&wil->napi_rx); |
588 | napi_disable(&wil->napi_tx); | |
589 | ||
2be7d22f | 590 | if (wil->scan_request) { |
2a91d7d0 VK |
591 | wil_dbg_misc(wil, "Abort scan_request 0x%p\n", |
592 | wil->scan_request); | |
047e5d74 | 593 | del_timer_sync(&wil->scan_timer); |
2be7d22f VK |
594 | cfg80211_scan_done(wil->scan_request, true); |
595 | wil->scan_request = NULL; | |
596 | } | |
597 | ||
598 | wil6210_disconnect(wil, NULL); | |
599 | wil_rx_fini(wil); | |
600 | ||
601 | return 0; | |
602 | } | |
603 | ||
604 | int wil_down(struct wil6210_priv *wil) | |
605 | { | |
606 | int rc; | |
607 | ||
608 | mutex_lock(&wil->mutex); | |
609 | rc = __wil_down(wil); | |
610 | mutex_unlock(&wil->mutex); | |
611 | ||
612 | return rc; | |
613 | } | |
3df2cd36 VK |
614 | |
615 | int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) | |
616 | { | |
617 | int i; | |
618 | int rc = -ENOENT; | |
619 | ||
620 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | |
621 | if ((wil->sta[i].status != wil_sta_unused) && | |
108d1eb6 | 622 | ether_addr_equal(wil->sta[i].addr, mac)) { |
3df2cd36 VK |
623 | rc = i; |
624 | break; | |
625 | } | |
626 | } | |
627 | ||
628 | return rc; | |
629 | } |