]>
Commit | Line | Data |
---|---|---|
d5c65159 KV |
1 | // SPDX-License-Identifier: BSD-3-Clause-Clear |
2 | /* | |
3 | * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. | |
4 | */ | |
5 | ||
6 | #include <linux/module.h> | |
7 | #include <linux/slab.h> | |
8 | #include <linux/remoteproc.h> | |
9 | #include <linux/firmware.h> | |
d5c65159 KV |
10 | #include "core.h" |
11 | #include "dp_tx.h" | |
9c57d7e3 | 12 | #include "dp_rx.h" |
d5c65159 | 13 | #include "debug.h" |
31858805 | 14 | #include "hif.h" |
d5c65159 KV |
15 | |
16 | unsigned int ath11k_debug_mask; | |
17 | module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); | |
18 | MODULE_PARM_DESC(debug_mask, "Debugging mask"); | |
19 | ||
d3318abf AK |
20 | static const struct ath11k_hw_params ath11k_hw_params[] = { |
21 | { | |
22 | .hw_rev = ATH11K_HW_IPQ8074, | |
23 | .name = "ipq8074 hw2.0", | |
24 | .fw = { | |
25 | .dir = IPQ8074_FW_DIR, | |
26 | .board_size = IPQ8074_MAX_BOARD_DATA_SZ, | |
27 | .cal_size = IPQ8074_MAX_CAL_DATA_SZ, | |
28 | }, | |
b1cc29e9 | 29 | .max_radios = 3, |
d547ca4c | 30 | .hw_ops = &ipq8074_ops, |
d5c65159 KV |
31 | }, |
32 | }; | |
33 | ||
d5c65159 KV |
34 | static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, |
35 | size_t name_len) | |
36 | { | |
37 | /* Note: bus is fixed to ahb. When other bus type supported, | |
38 | * make it to dynamic. | |
39 | */ | |
40 | scnprintf(name, name_len, | |
41 | "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d", | |
42 | ab->qmi.target.chip_id, | |
43 | ab->qmi.target.board_id); | |
44 | ||
45 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name); | |
46 | ||
47 | return 0; | |
48 | } | |
49 | ||
50 | static const struct firmware *ath11k_fetch_fw_file(struct ath11k_base *ab, | |
51 | const char *dir, | |
52 | const char *file) | |
53 | { | |
54 | char filename[100]; | |
55 | const struct firmware *fw; | |
56 | int ret; | |
57 | ||
58 | if (file == NULL) | |
59 | return ERR_PTR(-ENOENT); | |
60 | ||
61 | if (dir == NULL) | |
62 | dir = "."; | |
63 | ||
64 | snprintf(filename, sizeof(filename), "%s/%s", dir, file); | |
65 | ret = firmware_request_nowarn(&fw, filename, ab->dev); | |
66 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot fw request '%s': %d\n", | |
67 | filename, ret); | |
68 | ||
69 | if (ret) | |
70 | return ERR_PTR(ret); | |
71 | ath11k_warn(ab, "Downloading BDF: %s, size: %zu\n", | |
72 | filename, fw->size); | |
73 | ||
74 | return fw; | |
75 | } | |
76 | ||
77 | void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) | |
78 | { | |
79 | if (!IS_ERR(bd->fw)) | |
80 | release_firmware(bd->fw); | |
81 | ||
82 | memset(bd, 0, sizeof(*bd)); | |
83 | } | |
84 | ||
85 | static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab, | |
86 | struct ath11k_board_data *bd, | |
87 | const void *buf, size_t buf_len, | |
88 | const char *boardname, | |
89 | int bd_ie_type) | |
90 | { | |
91 | const struct ath11k_fw_ie *hdr; | |
92 | bool name_match_found; | |
93 | int ret, board_ie_id; | |
94 | size_t board_ie_len; | |
95 | const void *board_ie_data; | |
96 | ||
97 | name_match_found = false; | |
98 | ||
99 | /* go through ATH11K_BD_IE_BOARD_ elements */ | |
100 | while (buf_len > sizeof(struct ath11k_fw_ie)) { | |
101 | hdr = buf; | |
102 | board_ie_id = le32_to_cpu(hdr->id); | |
103 | board_ie_len = le32_to_cpu(hdr->len); | |
104 | board_ie_data = hdr->data; | |
105 | ||
106 | buf_len -= sizeof(*hdr); | |
107 | buf += sizeof(*hdr); | |
108 | ||
109 | if (buf_len < ALIGN(board_ie_len, 4)) { | |
110 | ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", | |
111 | buf_len, ALIGN(board_ie_len, 4)); | |
112 | ret = -EINVAL; | |
113 | goto out; | |
114 | } | |
115 | ||
116 | switch (board_ie_id) { | |
117 | case ATH11K_BD_IE_BOARD_NAME: | |
118 | ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", | |
119 | board_ie_data, board_ie_len); | |
120 | ||
121 | if (board_ie_len != strlen(boardname)) | |
122 | break; | |
123 | ||
124 | ret = memcmp(board_ie_data, boardname, strlen(boardname)); | |
125 | if (ret) | |
126 | break; | |
127 | ||
128 | name_match_found = true; | |
129 | ath11k_dbg(ab, ATH11K_DBG_BOOT, | |
130 | "boot found match for name '%s'", | |
131 | boardname); | |
132 | break; | |
133 | case ATH11K_BD_IE_BOARD_DATA: | |
134 | if (!name_match_found) | |
135 | /* no match found */ | |
136 | break; | |
137 | ||
138 | ath11k_dbg(ab, ATH11K_DBG_BOOT, | |
139 | "boot found board data for '%s'", boardname); | |
140 | ||
141 | bd->data = board_ie_data; | |
142 | bd->len = board_ie_len; | |
143 | ||
144 | ret = 0; | |
145 | goto out; | |
146 | default: | |
147 | ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", | |
148 | board_ie_id); | |
149 | break; | |
150 | } | |
151 | ||
152 | /* jump over the padding */ | |
153 | board_ie_len = ALIGN(board_ie_len, 4); | |
154 | ||
155 | buf_len -= board_ie_len; | |
156 | buf += board_ie_len; | |
157 | } | |
158 | ||
159 | /* no match found */ | |
160 | ret = -ENOENT; | |
161 | ||
162 | out: | |
163 | return ret; | |
164 | } | |
165 | ||
166 | static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, | |
167 | struct ath11k_board_data *bd, | |
168 | const char *boardname) | |
169 | { | |
170 | size_t len, magic_len; | |
171 | const u8 *data; | |
172 | char *filename = ATH11K_BOARD_API2_FILE; | |
173 | size_t ie_len; | |
174 | struct ath11k_fw_ie *hdr; | |
175 | int ret, ie_id; | |
176 | ||
177 | if (!bd->fw) | |
178 | bd->fw = ath11k_fetch_fw_file(ab, | |
179 | ab->hw_params.fw.dir, | |
180 | filename); | |
181 | if (IS_ERR(bd->fw)) | |
182 | return PTR_ERR(bd->fw); | |
183 | ||
184 | data = bd->fw->data; | |
185 | len = bd->fw->size; | |
186 | ||
187 | /* magic has extra null byte padded */ | |
188 | magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; | |
189 | if (len < magic_len) { | |
190 | ath11k_err(ab, "failed to find magic value in %s/%s, file too short: %zu\n", | |
191 | ab->hw_params.fw.dir, filename, len); | |
192 | ret = -EINVAL; | |
193 | goto err; | |
194 | } | |
195 | ||
196 | if (memcmp(data, ATH11K_BOARD_MAGIC, magic_len)) { | |
197 | ath11k_err(ab, "found invalid board magic\n"); | |
198 | ret = -EINVAL; | |
199 | goto err; | |
200 | } | |
201 | ||
202 | /* magic is padded to 4 bytes */ | |
203 | magic_len = ALIGN(magic_len, 4); | |
204 | if (len < magic_len) { | |
205 | ath11k_err(ab, "failed: %s/%s too small to contain board data, len: %zu\n", | |
206 | ab->hw_params.fw.dir, filename, len); | |
207 | ret = -EINVAL; | |
208 | goto err; | |
209 | } | |
210 | ||
211 | data += magic_len; | |
212 | len -= magic_len; | |
213 | ||
214 | while (len > sizeof(struct ath11k_fw_ie)) { | |
215 | hdr = (struct ath11k_fw_ie *)data; | |
216 | ie_id = le32_to_cpu(hdr->id); | |
217 | ie_len = le32_to_cpu(hdr->len); | |
218 | ||
219 | len -= sizeof(*hdr); | |
220 | data = hdr->data; | |
221 | ||
222 | if (len < ALIGN(ie_len, 4)) { | |
223 | ath11k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n", | |
224 | ie_id, ie_len, len); | |
225 | return -EINVAL; | |
226 | } | |
227 | ||
228 | switch (ie_id) { | |
229 | case ATH11K_BD_IE_BOARD: | |
230 | ret = ath11k_core_parse_bd_ie_board(ab, bd, data, | |
231 | ie_len, | |
232 | boardname, | |
233 | ATH11K_BD_IE_BOARD); | |
234 | if (ret == -ENOENT) | |
235 | /* no match found, continue */ | |
236 | break; | |
237 | else if (ret) | |
238 | /* there was an error, bail out */ | |
239 | goto err; | |
240 | /* either found or error, so stop searching */ | |
241 | goto out; | |
242 | } | |
243 | ||
244 | /* jump over the padding */ | |
245 | ie_len = ALIGN(ie_len, 4); | |
246 | ||
247 | len -= ie_len; | |
248 | data += ie_len; | |
249 | } | |
250 | ||
251 | out: | |
252 | if (!bd->data || !bd->len) { | |
253 | ath11k_err(ab, | |
254 | "failed to fetch board data for %s from %s/%s\n", | |
255 | boardname, ab->hw_params.fw.dir, filename); | |
256 | ret = -ENODATA; | |
257 | goto err; | |
258 | } | |
259 | ||
260 | return 0; | |
261 | ||
262 | err: | |
263 | ath11k_core_free_bdf(ab, bd); | |
264 | return ret; | |
265 | } | |
266 | ||
267 | static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, | |
268 | struct ath11k_board_data *bd) | |
269 | { | |
270 | bd->fw = ath11k_fetch_fw_file(ab, | |
271 | ab->hw_params.fw.dir, | |
272 | ATH11K_DEFAULT_BOARD_FILE); | |
273 | if (IS_ERR(bd->fw)) | |
274 | return PTR_ERR(bd->fw); | |
275 | ||
276 | bd->data = bd->fw->data; | |
277 | bd->len = bd->fw->size; | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | #define BOARD_NAME_SIZE 100 | |
283 | int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) | |
284 | { | |
285 | char boardname[BOARD_NAME_SIZE]; | |
286 | int ret; | |
287 | ||
288 | ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); | |
289 | if (ret) { | |
290 | ath11k_err(ab, "failed to create board name: %d", ret); | |
291 | return ret; | |
292 | } | |
293 | ||
294 | ab->bd_api = 2; | |
295 | ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); | |
296 | if (!ret) | |
297 | goto success; | |
298 | ||
299 | ab->bd_api = 1; | |
300 | ret = ath11k_core_fetch_board_data_api_1(ab, bd); | |
301 | if (ret) { | |
302 | ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", | |
303 | ab->hw_params.fw.dir); | |
304 | return ret; | |
305 | } | |
306 | ||
307 | success: | |
308 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "using board api %d\n", ab->bd_api); | |
309 | return 0; | |
310 | } | |
311 | ||
312 | static void ath11k_core_stop(struct ath11k_base *ab) | |
313 | { | |
314 | if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) | |
315 | ath11k_qmi_firmware_stop(ab); | |
31858805 | 316 | ath11k_hif_stop(ab); |
d5c65159 | 317 | ath11k_wmi_detach(ab); |
9c57d7e3 | 318 | ath11k_dp_pdev_reo_cleanup(ab); |
d5c65159 KV |
319 | |
320 | /* De-Init of components as needed */ | |
321 | } | |
322 | ||
323 | static int ath11k_core_soc_create(struct ath11k_base *ab) | |
324 | { | |
325 | int ret; | |
326 | ||
327 | ret = ath11k_qmi_init_service(ab); | |
328 | if (ret) { | |
329 | ath11k_err(ab, "failed to initialize qmi :%d\n", ret); | |
330 | return ret; | |
331 | } | |
332 | ||
333 | ret = ath11k_debug_soc_create(ab); | |
334 | if (ret) { | |
335 | ath11k_err(ab, "failed to create ath11k debugfs\n"); | |
336 | goto err_qmi_deinit; | |
337 | } | |
338 | ||
31858805 | 339 | ret = ath11k_hif_power_up(ab); |
d5c65159 KV |
340 | if (ret) { |
341 | ath11k_err(ab, "failed to power up :%d\n", ret); | |
342 | goto err_debugfs_reg; | |
343 | } | |
344 | ||
345 | return 0; | |
346 | ||
347 | err_debugfs_reg: | |
348 | ath11k_debug_soc_destroy(ab); | |
349 | err_qmi_deinit: | |
350 | ath11k_qmi_deinit_service(ab); | |
351 | return ret; | |
352 | } | |
353 | ||
354 | static void ath11k_core_soc_destroy(struct ath11k_base *ab) | |
355 | { | |
356 | ath11k_debug_soc_destroy(ab); | |
357 | ath11k_dp_free(ab); | |
358 | ath11k_reg_free(ab); | |
359 | ath11k_qmi_deinit_service(ab); | |
360 | } | |
361 | ||
362 | static int ath11k_core_pdev_create(struct ath11k_base *ab) | |
363 | { | |
364 | int ret; | |
365 | ||
366 | ret = ath11k_debug_pdev_create(ab); | |
367 | if (ret) { | |
368 | ath11k_err(ab, "failed to create core pdev debugfs: %d\n", ret); | |
369 | return ret; | |
370 | } | |
371 | ||
0366f426 | 372 | ret = ath11k_mac_register(ab); |
d5c65159 | 373 | if (ret) { |
0366f426 | 374 | ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); |
d5c65159 KV |
375 | goto err_pdev_debug; |
376 | } | |
377 | ||
378 | ret = ath11k_dp_pdev_alloc(ab); | |
379 | if (ret) { | |
380 | ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); | |
0366f426 | 381 | goto err_mac_unregister; |
d5c65159 KV |
382 | } |
383 | ||
2a63bbca PKC |
384 | ret = ath11k_thermal_register(ab); |
385 | if (ret) { | |
386 | ath11k_err(ab, "could not register thermal device: %d\n", | |
387 | ret); | |
388 | goto err_dp_pdev_free; | |
389 | } | |
390 | ||
9d11b7bf KP |
391 | ret = ath11k_spectral_init(ab); |
392 | if (ret) { | |
393 | ath11k_err(ab, "failed to init spectral %d\n", ret); | |
394 | goto err_thermal_unregister; | |
395 | } | |
396 | ||
d5c65159 KV |
397 | return 0; |
398 | ||
9d11b7bf KP |
399 | err_thermal_unregister: |
400 | ath11k_thermal_unregister(ab); | |
2a63bbca PKC |
401 | err_dp_pdev_free: |
402 | ath11k_dp_pdev_free(ab); | |
0366f426 VT |
403 | err_mac_unregister: |
404 | ath11k_mac_unregister(ab); | |
d5c65159 KV |
405 | err_pdev_debug: |
406 | ath11k_debug_pdev_destroy(ab); | |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
411 | static void ath11k_core_pdev_destroy(struct ath11k_base *ab) | |
412 | { | |
9d11b7bf | 413 | ath11k_spectral_deinit(ab); |
2a63bbca | 414 | ath11k_thermal_unregister(ab); |
d5c65159 | 415 | ath11k_mac_unregister(ab); |
31858805 | 416 | ath11k_hif_irq_disable(ab); |
d5c65159 KV |
417 | ath11k_dp_pdev_free(ab); |
418 | ath11k_debug_pdev_destroy(ab); | |
419 | } | |
420 | ||
421 | static int ath11k_core_start(struct ath11k_base *ab, | |
422 | enum ath11k_firmware_mode mode) | |
423 | { | |
424 | int ret; | |
425 | ||
426 | ret = ath11k_qmi_firmware_start(ab, mode); | |
427 | if (ret) { | |
428 | ath11k_err(ab, "failed to attach wmi: %d\n", ret); | |
429 | return ret; | |
430 | } | |
431 | ||
432 | ret = ath11k_wmi_attach(ab); | |
433 | if (ret) { | |
434 | ath11k_err(ab, "failed to attach wmi: %d\n", ret); | |
435 | goto err_firmware_stop; | |
436 | } | |
437 | ||
438 | ret = ath11k_htc_init(ab); | |
439 | if (ret) { | |
440 | ath11k_err(ab, "failed to init htc: %d\n", ret); | |
441 | goto err_wmi_detach; | |
442 | } | |
443 | ||
31858805 | 444 | ret = ath11k_hif_start(ab); |
d5c65159 KV |
445 | if (ret) { |
446 | ath11k_err(ab, "failed to start HIF: %d\n", ret); | |
447 | goto err_wmi_detach; | |
448 | } | |
449 | ||
450 | ret = ath11k_htc_wait_target(&ab->htc); | |
451 | if (ret) { | |
452 | ath11k_err(ab, "failed to connect to HTC: %d\n", ret); | |
453 | goto err_hif_stop; | |
454 | } | |
455 | ||
456 | ret = ath11k_dp_htt_connect(&ab->dp); | |
457 | if (ret) { | |
458 | ath11k_err(ab, "failed to connect to HTT: %d\n", ret); | |
459 | goto err_hif_stop; | |
460 | } | |
461 | ||
462 | ret = ath11k_wmi_connect(ab); | |
463 | if (ret) { | |
464 | ath11k_err(ab, "failed to connect wmi: %d\n", ret); | |
465 | goto err_hif_stop; | |
466 | } | |
467 | ||
468 | ret = ath11k_htc_start(&ab->htc); | |
469 | if (ret) { | |
470 | ath11k_err(ab, "failed to start HTC: %d\n", ret); | |
471 | goto err_hif_stop; | |
472 | } | |
473 | ||
474 | ret = ath11k_wmi_wait_for_service_ready(ab); | |
475 | if (ret) { | |
476 | ath11k_err(ab, "failed to receive wmi service ready event: %d\n", | |
477 | ret); | |
478 | goto err_hif_stop; | |
479 | } | |
480 | ||
0366f426 VT |
481 | ret = ath11k_mac_allocate(ab); |
482 | if (ret) { | |
483 | ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", | |
484 | ret); | |
485 | goto err_hif_stop; | |
486 | } | |
487 | ||
9c57d7e3 VT |
488 | ath11k_dp_pdev_pre_alloc(ab); |
489 | ||
490 | ret = ath11k_dp_pdev_reo_setup(ab); | |
491 | if (ret) { | |
492 | ath11k_err(ab, "failed to initialize reo destination rings: %d\n", ret); | |
493 | goto err_mac_destroy; | |
494 | } | |
495 | ||
d5c65159 KV |
496 | ret = ath11k_wmi_cmd_init(ab); |
497 | if (ret) { | |
498 | ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); | |
9c57d7e3 | 499 | goto err_reo_cleanup; |
d5c65159 KV |
500 | } |
501 | ||
502 | ret = ath11k_wmi_wait_for_unified_ready(ab); | |
503 | if (ret) { | |
504 | ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", | |
505 | ret); | |
9c57d7e3 | 506 | goto err_reo_cleanup; |
d5c65159 KV |
507 | } |
508 | ||
509 | ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); | |
510 | if (ret) { | |
511 | ath11k_err(ab, "failed to send htt version request message: %d\n", | |
512 | ret); | |
9c57d7e3 | 513 | goto err_reo_cleanup; |
d5c65159 KV |
514 | } |
515 | ||
516 | return 0; | |
517 | ||
9c57d7e3 VT |
518 | err_reo_cleanup: |
519 | ath11k_dp_pdev_reo_cleanup(ab); | |
0366f426 VT |
520 | err_mac_destroy: |
521 | ath11k_mac_destroy(ab); | |
d5c65159 | 522 | err_hif_stop: |
31858805 | 523 | ath11k_hif_stop(ab); |
d5c65159 KV |
524 | err_wmi_detach: |
525 | ath11k_wmi_detach(ab); | |
526 | err_firmware_stop: | |
527 | ath11k_qmi_firmware_stop(ab); | |
528 | ||
529 | return ret; | |
530 | } | |
531 | ||
532 | int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) | |
533 | { | |
534 | int ret; | |
535 | ||
536 | ret = ath11k_ce_init_pipes(ab); | |
537 | if (ret) { | |
538 | ath11k_err(ab, "failed to initialize CE: %d\n", ret); | |
539 | return ret; | |
540 | } | |
541 | ||
542 | ret = ath11k_dp_alloc(ab); | |
543 | if (ret) { | |
544 | ath11k_err(ab, "failed to init DP: %d\n", ret); | |
545 | return ret; | |
546 | } | |
547 | ||
548 | mutex_lock(&ab->core_lock); | |
549 | ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); | |
550 | if (ret) { | |
551 | ath11k_err(ab, "failed to start core: %d\n", ret); | |
552 | goto err_dp_free; | |
553 | } | |
554 | ||
555 | ret = ath11k_core_pdev_create(ab); | |
556 | if (ret) { | |
557 | ath11k_err(ab, "failed to create pdev core: %d\n", ret); | |
558 | goto err_core_stop; | |
559 | } | |
31858805 | 560 | ath11k_hif_irq_enable(ab); |
d5c65159 KV |
561 | mutex_unlock(&ab->core_lock); |
562 | ||
563 | return 0; | |
564 | ||
565 | err_core_stop: | |
566 | ath11k_core_stop(ab); | |
0366f426 | 567 | ath11k_mac_destroy(ab); |
d5c65159 KV |
568 | err_dp_free: |
569 | ath11k_dp_free(ab); | |
ba479239 | 570 | mutex_unlock(&ab->core_lock); |
d5c65159 KV |
571 | return ret; |
572 | } | |
573 | ||
574 | static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) | |
575 | { | |
576 | int ret; | |
577 | ||
578 | mutex_lock(&ab->core_lock); | |
2a63bbca | 579 | ath11k_thermal_unregister(ab); |
31858805 | 580 | ath11k_hif_irq_disable(ab); |
d5c65159 | 581 | ath11k_dp_pdev_free(ab); |
9d11b7bf | 582 | ath11k_spectral_deinit(ab); |
31858805 | 583 | ath11k_hif_stop(ab); |
d5c65159 | 584 | ath11k_wmi_detach(ab); |
9c57d7e3 | 585 | ath11k_dp_pdev_reo_cleanup(ab); |
d5c65159 KV |
586 | mutex_unlock(&ab->core_lock); |
587 | ||
588 | ath11k_dp_free(ab); | |
589 | ath11k_hal_srng_deinit(ab); | |
590 | ||
591 | ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; | |
592 | ||
593 | ret = ath11k_hal_srng_init(ab); | |
594 | if (ret) | |
595 | return ret; | |
596 | ||
597 | clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); | |
598 | ||
599 | ret = ath11k_core_qmi_firmware_ready(ab); | |
600 | if (ret) | |
601 | goto err_hal_srng_deinit; | |
602 | ||
603 | clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); | |
604 | ||
605 | return 0; | |
606 | ||
607 | err_hal_srng_deinit: | |
608 | ath11k_hal_srng_deinit(ab); | |
609 | return ret; | |
610 | } | |
611 | ||
612 | void ath11k_core_halt(struct ath11k *ar) | |
613 | { | |
614 | struct ath11k_base *ab = ar->ab; | |
615 | ||
616 | lockdep_assert_held(&ar->conf_mutex); | |
617 | ||
618 | ar->num_created_vdevs = 0; | |
79c080db | 619 | ar->allocated_vdev_map = 0; |
d5c65159 KV |
620 | |
621 | ath11k_mac_scan_finish(ar); | |
622 | ath11k_mac_peer_cleanup_all(ar); | |
623 | cancel_delayed_work_sync(&ar->scan.timeout); | |
624 | cancel_work_sync(&ar->regd_update_work); | |
625 | ||
626 | rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); | |
627 | synchronize_rcu(); | |
628 | INIT_LIST_HEAD(&ar->arvifs); | |
629 | idr_init(&ar->txmgmt_idr); | |
630 | } | |
631 | ||
632 | static void ath11k_core_restart(struct work_struct *work) | |
633 | { | |
634 | struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); | |
635 | struct ath11k *ar; | |
636 | struct ath11k_pdev *pdev; | |
637 | int i, ret = 0; | |
638 | ||
639 | spin_lock_bh(&ab->base_lock); | |
640 | ab->stats.fw_crash_counter++; | |
641 | spin_unlock_bh(&ab->base_lock); | |
642 | ||
643 | for (i = 0; i < ab->num_radios; i++) { | |
644 | pdev = &ab->pdevs[i]; | |
645 | ar = pdev->ar; | |
646 | if (!ar || ar->state == ATH11K_STATE_OFF) | |
647 | continue; | |
648 | ||
649 | ieee80211_stop_queues(ar->hw); | |
650 | ath11k_mac_drain_tx(ar); | |
651 | complete(&ar->scan.started); | |
652 | complete(&ar->scan.completed); | |
653 | complete(&ar->peer_assoc_done); | |
654 | complete(&ar->install_key_done); | |
655 | complete(&ar->vdev_setup_done); | |
656 | complete(&ar->bss_survey_done); | |
a41d1034 | 657 | complete(&ar->thermal.wmi_sync); |
d5c65159 KV |
658 | |
659 | wake_up(&ar->dp.tx_empty_waitq); | |
660 | idr_for_each(&ar->txmgmt_idr, | |
661 | ath11k_mac_tx_mgmt_pending_free, ar); | |
662 | idr_destroy(&ar->txmgmt_idr); | |
663 | } | |
664 | ||
6bc9d6f7 | 665 | wake_up(&ab->wmi_ab.tx_credits_wq); |
d5c65159 KV |
666 | wake_up(&ab->peer_mapping_wq); |
667 | ||
668 | ret = ath11k_core_reconfigure_on_crash(ab); | |
669 | if (ret) { | |
670 | ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); | |
671 | return; | |
672 | } | |
673 | ||
674 | for (i = 0; i < ab->num_radios; i++) { | |
675 | pdev = &ab->pdevs[i]; | |
676 | ar = pdev->ar; | |
677 | if (!ar || ar->state == ATH11K_STATE_OFF) | |
678 | continue; | |
679 | ||
680 | mutex_lock(&ar->conf_mutex); | |
681 | ||
682 | switch (ar->state) { | |
683 | case ATH11K_STATE_ON: | |
684 | ar->state = ATH11K_STATE_RESTARTING; | |
685 | ath11k_core_halt(ar); | |
686 | ieee80211_restart_hw(ar->hw); | |
687 | break; | |
688 | case ATH11K_STATE_OFF: | |
689 | ath11k_warn(ab, | |
690 | "cannot restart radio %d that hasn't been started\n", | |
691 | i); | |
692 | break; | |
693 | case ATH11K_STATE_RESTARTING: | |
694 | break; | |
695 | case ATH11K_STATE_RESTARTED: | |
696 | ar->state = ATH11K_STATE_WEDGED; | |
697 | /* fall through */ | |
698 | case ATH11K_STATE_WEDGED: | |
699 | ath11k_warn(ab, | |
700 | "device is wedged, will not restart radio %d\n", i); | |
701 | break; | |
702 | } | |
703 | mutex_unlock(&ar->conf_mutex); | |
704 | } | |
705 | complete(&ab->driver_recovery); | |
706 | } | |
707 | ||
d3318abf AK |
708 | static int ath11k_init_hw_params(struct ath11k_base *ab) |
709 | { | |
710 | const struct ath11k_hw_params *hw_params = NULL; | |
711 | int i; | |
712 | ||
713 | for (i = 0; i < ARRAY_SIZE(ath11k_hw_params); i++) { | |
714 | hw_params = &ath11k_hw_params[i]; | |
715 | ||
716 | if (hw_params->hw_rev == ab->hw_rev) | |
717 | break; | |
718 | } | |
719 | ||
720 | if (i == ARRAY_SIZE(ath11k_hw_params)) { | |
721 | ath11k_err(ab, "Unsupported hardware version: 0x%x\n", ab->hw_rev); | |
722 | return -EINVAL; | |
723 | } | |
724 | ||
725 | ab->hw_params = *hw_params; | |
726 | ||
727 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "Hardware name %s\n", ab->hw_params.name); | |
728 | ||
729 | return 0; | |
730 | } | |
731 | ||
d5c65159 KV |
732 | int ath11k_core_init(struct ath11k_base *ab) |
733 | { | |
734 | struct device *dev = ab->dev; | |
735 | struct rproc *prproc; | |
736 | phandle rproc_phandle; | |
737 | int ret; | |
738 | ||
739 | if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { | |
740 | ath11k_err(ab, "failed to get q6_rproc handle\n"); | |
741 | return -ENOENT; | |
742 | } | |
743 | ||
744 | prproc = rproc_get_by_phandle(rproc_phandle); | |
745 | if (!prproc) { | |
746 | ath11k_err(ab, "failed to get rproc\n"); | |
747 | return -EINVAL; | |
748 | } | |
749 | ab->tgt_rproc = prproc; | |
d3318abf AK |
750 | |
751 | ret = ath11k_init_hw_params(ab); | |
752 | if (ret) { | |
753 | ath11k_err(ab, "failed to get hw params %d\n", ret); | |
754 | return ret; | |
755 | } | |
d5c65159 KV |
756 | |
757 | ret = ath11k_core_soc_create(ab); | |
758 | if (ret) { | |
759 | ath11k_err(ab, "failed to create soc core: %d\n", ret); | |
760 | return ret; | |
761 | } | |
762 | ||
763 | return 0; | |
764 | } | |
765 | ||
766 | void ath11k_core_deinit(struct ath11k_base *ab) | |
767 | { | |
768 | mutex_lock(&ab->core_lock); | |
769 | ||
770 | ath11k_core_pdev_destroy(ab); | |
771 | ath11k_core_stop(ab); | |
772 | ||
773 | mutex_unlock(&ab->core_lock); | |
774 | ||
31858805 | 775 | ath11k_hif_power_down(ab); |
d5c65159 KV |
776 | ath11k_mac_destroy(ab); |
777 | ath11k_core_soc_destroy(ab); | |
778 | } | |
779 | ||
780 | void ath11k_core_free(struct ath11k_base *ab) | |
781 | { | |
782 | kfree(ab); | |
783 | } | |
784 | ||
630ad41c GS |
785 | struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, |
786 | enum ath11k_bus bus) | |
d5c65159 KV |
787 | { |
788 | struct ath11k_base *ab; | |
789 | ||
630ad41c | 790 | ab = kzalloc(sizeof(*ab) + priv_size, GFP_KERNEL); |
d5c65159 KV |
791 | if (!ab) |
792 | return NULL; | |
793 | ||
794 | init_completion(&ab->driver_recovery); | |
795 | ||
796 | ab->workqueue = create_singlethread_workqueue("ath11k_wq"); | |
797 | if (!ab->workqueue) | |
798 | goto err_sc_free; | |
799 | ||
800 | mutex_init(&ab->core_lock); | |
801 | spin_lock_init(&ab->base_lock); | |
802 | ||
803 | INIT_LIST_HEAD(&ab->peers); | |
804 | init_waitqueue_head(&ab->peer_mapping_wq); | |
6bc9d6f7 | 805 | init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); |
d5c65159 KV |
806 | INIT_WORK(&ab->restart_work, ath11k_core_restart); |
807 | timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); | |
808 | ab->dev = dev; | |
809 | ||
810 | return ab; | |
811 | ||
812 | err_sc_free: | |
813 | kfree(ab); | |
814 | return NULL; | |
815 | } |