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