1 | // SPDX-License-Identifier: BSD-3-Clause-Clear |
2 | /* |
3 | * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. |
4 | * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. |
5 | */ |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/remoteproc.h> |
10 | #include <linux/firmware.h> |
11 | #include <linux/of.h> |
12 | #include "core.h" |
13 | #include "dp_tx.h" |
14 | #include "dp_rx.h" |
15 | #include "debug.h" |
16 | #include "hif.h" |
17 | #include "fw.h" |
18 | |
19 | unsigned int ath12k_debug_mask; |
20 | module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); |
21 | MODULE_PARM_DESC(debug_mask, "Debugging mask" ); |
22 | |
23 | static int ath12k_core_rfkill_config(struct ath12k_base *ab) |
24 | { |
25 | struct ath12k *ar; |
26 | int ret = 0, i; |
27 | |
28 | if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) |
29 | return 0; |
30 | |
31 | for (i = 0; i < ab->num_radios; i++) { |
32 | ar = ab->pdevs[i].ar; |
33 | |
34 | ret = ath12k_mac_rfkill_config(ar); |
35 | if (ret && ret != -EOPNOTSUPP) { |
36 | ath12k_warn(ab, fmt: "failed to configure rfkill: %d" , ret); |
37 | return ret; |
38 | } |
39 | } |
40 | |
41 | return ret; |
42 | } |
43 | |
44 | int ath12k_core_suspend(struct ath12k_base *ab) |
45 | { |
46 | int ret; |
47 | |
48 | if (!ab->hw_params->supports_suspend) |
49 | return -EOPNOTSUPP; |
50 | |
51 | /* TODO: there can frames in queues so for now add delay as a hack. |
52 | * Need to implement to handle and remove this delay. |
53 | */ |
54 | msleep(msecs: 500); |
55 | |
56 | ret = ath12k_dp_rx_pktlog_stop(ab, stop_timer: true); |
57 | if (ret) { |
58 | ath12k_warn(ab, fmt: "failed to stop dp rx (and timer) pktlog during suspend: %d\n" , |
59 | ret); |
60 | return ret; |
61 | } |
62 | |
63 | ret = ath12k_dp_rx_pktlog_stop(ab, stop_timer: false); |
64 | if (ret) { |
65 | ath12k_warn(ab, fmt: "failed to stop dp rx pktlog during suspend: %d\n" , |
66 | ret); |
67 | return ret; |
68 | } |
69 | |
70 | ath12k_hif_irq_disable(ab); |
71 | ath12k_hif_ce_irq_disable(ab); |
72 | |
73 | ret = ath12k_hif_suspend(ab); |
74 | if (ret) { |
75 | ath12k_warn(ab, fmt: "failed to suspend hif: %d\n" , ret); |
76 | return ret; |
77 | } |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | int ath12k_core_resume(struct ath12k_base *ab) |
83 | { |
84 | int ret; |
85 | |
86 | if (!ab->hw_params->supports_suspend) |
87 | return -EOPNOTSUPP; |
88 | |
89 | ret = ath12k_hif_resume(ab); |
90 | if (ret) { |
91 | ath12k_warn(ab, fmt: "failed to resume hif during resume: %d\n" , ret); |
92 | return ret; |
93 | } |
94 | |
95 | ath12k_hif_ce_irq_enable(ab); |
96 | ath12k_hif_irq_enable(ab); |
97 | |
98 | ret = ath12k_dp_rx_pktlog_start(ab); |
99 | if (ret) { |
100 | ath12k_warn(ab, fmt: "failed to start rx pktlog during resume: %d\n" , |
101 | ret); |
102 | return ret; |
103 | } |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, |
109 | size_t name_len, bool with_variant, |
110 | bool bus_type_mode) |
111 | { |
112 | /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ |
113 | char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; |
114 | |
115 | if (with_variant && ab->qmi.target.bdf_ext[0] != '\0') |
116 | scnprintf(buf: variant, size: sizeof(variant), fmt: ",variant=%s" , |
117 | ab->qmi.target.bdf_ext); |
118 | |
119 | switch (ab->id.bdf_search) { |
120 | case ATH12K_BDF_SEARCH_BUS_AND_BOARD: |
121 | if (bus_type_mode) |
122 | scnprintf(buf: name, size: name_len, |
123 | fmt: "bus=%s" , |
124 | ath12k_bus_str(bus: ab->hif.bus)); |
125 | else |
126 | scnprintf(buf: name, size: name_len, |
127 | fmt: "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x,qmi-chip-id=%d,qmi-board-id=%d%s" , |
128 | ath12k_bus_str(bus: ab->hif.bus), |
129 | ab->id.vendor, ab->id.device, |
130 | ab->id.subsystem_vendor, |
131 | ab->id.subsystem_device, |
132 | ab->qmi.target.chip_id, |
133 | ab->qmi.target.board_id, |
134 | variant); |
135 | break; |
136 | default: |
137 | scnprintf(buf: name, size: name_len, |
138 | fmt: "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s" , |
139 | ath12k_bus_str(bus: ab->hif.bus), |
140 | ab->qmi.target.chip_id, |
141 | ab->qmi.target.board_id, variant); |
142 | break; |
143 | } |
144 | |
145 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot using board name '%s'\n" , name); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, |
151 | size_t name_len) |
152 | { |
153 | return __ath12k_core_create_board_name(ab, name, name_len, with_variant: true, bus_type_mode: false); |
154 | } |
155 | |
156 | static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, |
157 | size_t name_len) |
158 | { |
159 | return __ath12k_core_create_board_name(ab, name, name_len, with_variant: false, bus_type_mode: false); |
160 | } |
161 | |
162 | static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name, |
163 | size_t name_len) |
164 | { |
165 | return __ath12k_core_create_board_name(ab, name, name_len, with_variant: false, bus_type_mode: true); |
166 | } |
167 | |
168 | const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, |
169 | const char *file) |
170 | { |
171 | const struct firmware *fw; |
172 | char path[100]; |
173 | int ret; |
174 | |
175 | if (!file) |
176 | return ERR_PTR(error: -ENOENT); |
177 | |
178 | ath12k_core_create_firmware_path(ab, filename: file, buf: path, buf_len: sizeof(path)); |
179 | |
180 | ret = firmware_request_nowarn(fw: &fw, name: path, device: ab->dev); |
181 | if (ret) |
182 | return ERR_PTR(error: ret); |
183 | |
184 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot firmware request %s size %zu\n" , |
185 | path, fw->size); |
186 | |
187 | return fw; |
188 | } |
189 | |
190 | void ath12k_core_free_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) |
191 | { |
192 | if (!IS_ERR(ptr: bd->fw)) |
193 | release_firmware(fw: bd->fw); |
194 | |
195 | memset(bd, 0, sizeof(*bd)); |
196 | } |
197 | |
198 | static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab, |
199 | struct ath12k_board_data *bd, |
200 | const void *buf, size_t buf_len, |
201 | const char *boardname, |
202 | int ie_id, |
203 | int name_id, |
204 | int data_id) |
205 | { |
206 | const struct ath12k_fw_ie *hdr; |
207 | bool name_match_found; |
208 | int ret, board_ie_id; |
209 | size_t board_ie_len; |
210 | const void *board_ie_data; |
211 | |
212 | name_match_found = false; |
213 | |
214 | /* go through ATH12K_BD_IE_BOARD_/ATH12K_BD_IE_REGDB_ elements */ |
215 | while (buf_len > sizeof(struct ath12k_fw_ie)) { |
216 | hdr = buf; |
217 | board_ie_id = le32_to_cpu(hdr->id); |
218 | board_ie_len = le32_to_cpu(hdr->len); |
219 | board_ie_data = hdr->data; |
220 | |
221 | buf_len -= sizeof(*hdr); |
222 | buf += sizeof(*hdr); |
223 | |
224 | if (buf_len < ALIGN(board_ie_len, 4)) { |
225 | ath12k_err(ab, fmt: "invalid %s length: %zu < %zu\n" , |
226 | ath12k_bd_ie_type_str(type: ie_id), |
227 | buf_len, ALIGN(board_ie_len, 4)); |
228 | ret = -EINVAL; |
229 | goto out; |
230 | } |
231 | |
232 | if (board_ie_id == name_id) { |
233 | ath12k_dbg_dump(ab, mask: ATH12K_DBG_BOOT, msg: "board name" , prefix: "" , |
234 | buf: board_ie_data, len: board_ie_len); |
235 | |
236 | if (board_ie_len != strlen(boardname)) |
237 | goto next; |
238 | |
239 | ret = memcmp(p: board_ie_data, q: boardname, strlen(boardname)); |
240 | if (ret) |
241 | goto next; |
242 | |
243 | name_match_found = true; |
244 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
245 | "boot found match %s for name '%s'" , |
246 | ath12k_bd_ie_type_str(ie_id), |
247 | boardname); |
248 | } else if (board_ie_id == data_id) { |
249 | if (!name_match_found) |
250 | /* no match found */ |
251 | goto next; |
252 | |
253 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
254 | "boot found %s for '%s'" , |
255 | ath12k_bd_ie_type_str(ie_id), |
256 | boardname); |
257 | |
258 | bd->data = board_ie_data; |
259 | bd->len = board_ie_len; |
260 | |
261 | ret = 0; |
262 | goto out; |
263 | } else { |
264 | ath12k_warn(ab, fmt: "unknown %s id found: %d\n" , |
265 | ath12k_bd_ie_type_str(type: ie_id), |
266 | board_ie_id); |
267 | } |
268 | next: |
269 | /* jump over the padding */ |
270 | board_ie_len = ALIGN(board_ie_len, 4); |
271 | |
272 | buf_len -= board_ie_len; |
273 | buf += board_ie_len; |
274 | } |
275 | |
276 | /* no match found */ |
277 | ret = -ENOENT; |
278 | |
279 | out: |
280 | return ret; |
281 | } |
282 | |
283 | static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab, |
284 | struct ath12k_board_data *bd, |
285 | const char *boardname, |
286 | int ie_id_match, |
287 | int name_id, |
288 | int data_id) |
289 | { |
290 | size_t len, magic_len; |
291 | const u8 *data; |
292 | char *filename, filepath[100]; |
293 | size_t ie_len; |
294 | struct ath12k_fw_ie *hdr; |
295 | int ret, ie_id; |
296 | |
297 | filename = ATH12K_BOARD_API2_FILE; |
298 | |
299 | if (!bd->fw) |
300 | bd->fw = ath12k_core_firmware_request(ab, file: filename); |
301 | |
302 | if (IS_ERR(ptr: bd->fw)) |
303 | return PTR_ERR(ptr: bd->fw); |
304 | |
305 | data = bd->fw->data; |
306 | len = bd->fw->size; |
307 | |
308 | ath12k_core_create_firmware_path(ab, filename, |
309 | buf: filepath, buf_len: sizeof(filepath)); |
310 | |
311 | /* magic has extra null byte padded */ |
312 | magic_len = strlen(ATH12K_BOARD_MAGIC) + 1; |
313 | if (len < magic_len) { |
314 | ath12k_err(ab, fmt: "failed to find magic value in %s, file too short: %zu\n" , |
315 | filepath, len); |
316 | ret = -EINVAL; |
317 | goto err; |
318 | } |
319 | |
320 | if (memcmp(p: data, ATH12K_BOARD_MAGIC, size: magic_len)) { |
321 | ath12k_err(ab, fmt: "found invalid board magic\n" ); |
322 | ret = -EINVAL; |
323 | goto err; |
324 | } |
325 | |
326 | /* magic is padded to 4 bytes */ |
327 | magic_len = ALIGN(magic_len, 4); |
328 | if (len < magic_len) { |
329 | ath12k_err(ab, fmt: "failed: %s too small to contain board data, len: %zu\n" , |
330 | filepath, len); |
331 | ret = -EINVAL; |
332 | goto err; |
333 | } |
334 | |
335 | data += magic_len; |
336 | len -= magic_len; |
337 | |
338 | while (len > sizeof(struct ath12k_fw_ie)) { |
339 | hdr = (struct ath12k_fw_ie *)data; |
340 | ie_id = le32_to_cpu(hdr->id); |
341 | ie_len = le32_to_cpu(hdr->len); |
342 | |
343 | len -= sizeof(*hdr); |
344 | data = hdr->data; |
345 | |
346 | if (len < ALIGN(ie_len, 4)) { |
347 | ath12k_err(ab, fmt: "invalid length for board ie_id %d ie_len %zu len %zu\n" , |
348 | ie_id, ie_len, len); |
349 | ret = -EINVAL; |
350 | goto err; |
351 | } |
352 | |
353 | if (ie_id == ie_id_match) { |
354 | ret = ath12k_core_parse_bd_ie_board(ab, bd, buf: data, |
355 | buf_len: ie_len, |
356 | boardname, |
357 | ie_id: ie_id_match, |
358 | name_id, |
359 | data_id); |
360 | if (ret == -ENOENT) |
361 | /* no match found, continue */ |
362 | goto next; |
363 | else if (ret) |
364 | /* there was an error, bail out */ |
365 | goto err; |
366 | /* either found or error, so stop searching */ |
367 | goto out; |
368 | } |
369 | next: |
370 | /* jump over the padding */ |
371 | ie_len = ALIGN(ie_len, 4); |
372 | |
373 | len -= ie_len; |
374 | data += ie_len; |
375 | } |
376 | |
377 | out: |
378 | if (!bd->data || !bd->len) { |
379 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
380 | "failed to fetch %s for %s from %s\n" , |
381 | ath12k_bd_ie_type_str(ie_id_match), |
382 | boardname, filepath); |
383 | ret = -ENODATA; |
384 | goto err; |
385 | } |
386 | |
387 | return 0; |
388 | |
389 | err: |
390 | ath12k_core_free_bdf(ab, bd); |
391 | return ret; |
392 | } |
393 | |
394 | int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab, |
395 | struct ath12k_board_data *bd, |
396 | char *filename) |
397 | { |
398 | bd->fw = ath12k_core_firmware_request(ab, file: filename); |
399 | if (IS_ERR(ptr: bd->fw)) |
400 | return PTR_ERR(ptr: bd->fw); |
401 | |
402 | bd->data = bd->fw->data; |
403 | bd->len = bd->fw->size; |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | #define BOARD_NAME_SIZE 200 |
409 | int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd) |
410 | { |
411 | char boardname[BOARD_NAME_SIZE], fallback_boardname[BOARD_NAME_SIZE]; |
412 | char *filename, filepath[100]; |
413 | int bd_api; |
414 | int ret; |
415 | |
416 | filename = ATH12K_BOARD_API2_FILE; |
417 | |
418 | ret = ath12k_core_create_board_name(ab, name: boardname, name_len: sizeof(boardname)); |
419 | if (ret) { |
420 | ath12k_err(ab, fmt: "failed to create board name: %d" , ret); |
421 | return ret; |
422 | } |
423 | |
424 | bd_api = 2; |
425 | ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname, |
426 | ie_id_match: ATH12K_BD_IE_BOARD, |
427 | name_id: ATH12K_BD_IE_BOARD_NAME, |
428 | data_id: ATH12K_BD_IE_BOARD_DATA); |
429 | if (!ret) |
430 | goto success; |
431 | |
432 | ret = ath12k_core_create_fallback_board_name(ab, name: fallback_boardname, |
433 | name_len: sizeof(fallback_boardname)); |
434 | if (ret) { |
435 | ath12k_err(ab, fmt: "failed to create fallback board name: %d" , ret); |
436 | return ret; |
437 | } |
438 | |
439 | ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname: fallback_boardname, |
440 | ie_id_match: ATH12K_BD_IE_BOARD, |
441 | name_id: ATH12K_BD_IE_BOARD_NAME, |
442 | data_id: ATH12K_BD_IE_BOARD_DATA); |
443 | if (!ret) |
444 | goto success; |
445 | |
446 | bd_api = 1; |
447 | ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_DEFAULT_BOARD_FILE); |
448 | if (ret) { |
449 | ath12k_core_create_firmware_path(ab, filename, |
450 | buf: filepath, buf_len: sizeof(filepath)); |
451 | ath12k_err(ab, fmt: "failed to fetch board data for %s from %s\n" , |
452 | boardname, filepath); |
453 | if (memcmp(p: boardname, q: fallback_boardname, strlen(boardname))) |
454 | ath12k_err(ab, fmt: "failed to fetch board data for %s from %s\n" , |
455 | fallback_boardname, filepath); |
456 | |
457 | ath12k_err(ab, fmt: "failed to fetch board.bin from %s\n" , |
458 | ab->hw_params->fw.dir); |
459 | return ret; |
460 | } |
461 | |
462 | success: |
463 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "using board api %d\n" , bd_api); |
464 | return 0; |
465 | } |
466 | |
467 | int ath12k_core_fetch_regdb(struct ath12k_base *ab, struct ath12k_board_data *bd) |
468 | { |
469 | char boardname[BOARD_NAME_SIZE], default_boardname[BOARD_NAME_SIZE]; |
470 | int ret; |
471 | |
472 | ret = ath12k_core_create_board_name(ab, name: boardname, BOARD_NAME_SIZE); |
473 | if (ret) { |
474 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
475 | "failed to create board name for regdb: %d" , ret); |
476 | goto exit; |
477 | } |
478 | |
479 | ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname, |
480 | ie_id_match: ATH12K_BD_IE_REGDB, |
481 | name_id: ATH12K_BD_IE_REGDB_NAME, |
482 | data_id: ATH12K_BD_IE_REGDB_DATA); |
483 | if (!ret) |
484 | goto exit; |
485 | |
486 | ret = ath12k_core_create_bus_type_board_name(ab, name: default_boardname, |
487 | BOARD_NAME_SIZE); |
488 | if (ret) { |
489 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
490 | "failed to create default board name for regdb: %d" , ret); |
491 | goto exit; |
492 | } |
493 | |
494 | ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname: default_boardname, |
495 | ie_id_match: ATH12K_BD_IE_REGDB, |
496 | name_id: ATH12K_BD_IE_REGDB_NAME, |
497 | data_id: ATH12K_BD_IE_REGDB_DATA); |
498 | if (!ret) |
499 | goto exit; |
500 | |
501 | ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_REGDB_FILE_NAME); |
502 | if (ret) |
503 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to fetch %s from %s\n" , |
504 | ATH12K_REGDB_FILE_NAME, ab->hw_params->fw.dir); |
505 | |
506 | exit: |
507 | if (!ret) |
508 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "fetched regdb\n" ); |
509 | |
510 | return ret; |
511 | } |
512 | |
513 | u32 ath12k_core_get_max_station_per_radio(struct ath12k_base *ab) |
514 | { |
515 | if (ab->num_radios == 2) |
516 | return TARGET_NUM_STATIONS_DBS; |
517 | else if (ab->num_radios == 3) |
518 | return TARGET_NUM_PEERS_PDEV_DBS_SBS; |
519 | return TARGET_NUM_STATIONS_SINGLE; |
520 | } |
521 | |
522 | u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab) |
523 | { |
524 | if (ab->num_radios == 2) |
525 | return TARGET_NUM_PEERS_PDEV_DBS; |
526 | else if (ab->num_radios == 3) |
527 | return TARGET_NUM_PEERS_PDEV_DBS_SBS; |
528 | return TARGET_NUM_PEERS_PDEV_SINGLE; |
529 | } |
530 | |
531 | u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab) |
532 | { |
533 | if (ab->num_radios == 2) |
534 | return TARGET_NUM_TIDS(DBS); |
535 | else if (ab->num_radios == 3) |
536 | return TARGET_NUM_TIDS(DBS_SBS); |
537 | return TARGET_NUM_TIDS(SINGLE); |
538 | } |
539 | |
540 | static void ath12k_core_stop(struct ath12k_base *ab) |
541 | { |
542 | if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) |
543 | ath12k_qmi_firmware_stop(ab); |
544 | |
545 | ath12k_hif_stop(ab); |
546 | ath12k_wmi_detach(ab); |
547 | ath12k_dp_rx_pdev_reo_cleanup(ab); |
548 | |
549 | /* De-Init of components as needed */ |
550 | } |
551 | |
552 | static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) |
553 | { |
554 | struct ath12k_base *ab = data; |
555 | const char *magic = ATH12K_SMBIOS_BDF_EXT_MAGIC; |
556 | struct ath12k_smbios_bdf *smbios = (struct ath12k_smbios_bdf *)hdr; |
557 | ssize_t copied; |
558 | size_t len; |
559 | int i; |
560 | |
561 | if (ab->qmi.target.bdf_ext[0] != '\0') |
562 | return; |
563 | |
564 | if (hdr->type != ATH12K_SMBIOS_BDF_EXT_TYPE) |
565 | return; |
566 | |
567 | if (hdr->length != ATH12K_SMBIOS_BDF_EXT_LENGTH) { |
568 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
569 | "wrong smbios bdf ext type length (%d).\n" , |
570 | hdr->length); |
571 | return; |
572 | } |
573 | |
574 | if (!smbios->bdf_enabled) { |
575 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "bdf variant name not found.\n" ); |
576 | return; |
577 | } |
578 | |
579 | /* Only one string exists (per spec) */ |
580 | if (memcmp(p: smbios->bdf_ext, q: magic, strlen(magic)) != 0) { |
581 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
582 | "bdf variant magic does not match.\n" ); |
583 | return; |
584 | } |
585 | |
586 | len = min_t(size_t, |
587 | strlen(smbios->bdf_ext), sizeof(ab->qmi.target.bdf_ext)); |
588 | for (i = 0; i < len; i++) { |
589 | if (!isascii(smbios->bdf_ext[i]) || !isprint(smbios->bdf_ext[i])) { |
590 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
591 | "bdf variant name contains non ascii chars.\n" ); |
592 | return; |
593 | } |
594 | } |
595 | |
596 | /* Copy extension name without magic prefix */ |
597 | copied = strscpy(ab->qmi.target.bdf_ext, smbios->bdf_ext + strlen(magic), |
598 | sizeof(ab->qmi.target.bdf_ext)); |
599 | if (copied < 0) { |
600 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
601 | "bdf variant string is longer than the buffer can accommodate\n" ); |
602 | return; |
603 | } |
604 | |
605 | ath12k_dbg(ab, ATH12K_DBG_BOOT, |
606 | "found and validated bdf variant smbios_type 0x%x bdf %s\n" , |
607 | ATH12K_SMBIOS_BDF_EXT_TYPE, ab->qmi.target.bdf_ext); |
608 | } |
609 | |
610 | int ath12k_core_check_smbios(struct ath12k_base *ab) |
611 | { |
612 | ab->qmi.target.bdf_ext[0] = '\0'; |
613 | dmi_walk(decode: ath12k_core_check_bdfext, private_data: ab); |
614 | |
615 | if (ab->qmi.target.bdf_ext[0] == '\0') |
616 | return -ENODATA; |
617 | |
618 | return 0; |
619 | } |
620 | |
621 | static int ath12k_core_soc_create(struct ath12k_base *ab) |
622 | { |
623 | int ret; |
624 | |
625 | ret = ath12k_qmi_init_service(ab); |
626 | if (ret) { |
627 | ath12k_err(ab, fmt: "failed to initialize qmi :%d\n" , ret); |
628 | return ret; |
629 | } |
630 | |
631 | ret = ath12k_hif_power_up(ab); |
632 | if (ret) { |
633 | ath12k_err(ab, fmt: "failed to power up :%d\n" , ret); |
634 | goto err_qmi_deinit; |
635 | } |
636 | |
637 | return 0; |
638 | |
639 | err_qmi_deinit: |
640 | ath12k_qmi_deinit_service(ab); |
641 | return ret; |
642 | } |
643 | |
644 | static void ath12k_core_soc_destroy(struct ath12k_base *ab) |
645 | { |
646 | ath12k_dp_free(ab); |
647 | ath12k_reg_free(ab); |
648 | ath12k_qmi_deinit_service(ab); |
649 | } |
650 | |
651 | static int ath12k_core_pdev_create(struct ath12k_base *ab) |
652 | { |
653 | int ret; |
654 | |
655 | ret = ath12k_mac_register(ab); |
656 | if (ret) { |
657 | ath12k_err(ab, fmt: "failed register the radio with mac80211: %d\n" , ret); |
658 | return ret; |
659 | } |
660 | |
661 | ret = ath12k_dp_pdev_alloc(ab); |
662 | if (ret) { |
663 | ath12k_err(ab, fmt: "failed to attach DP pdev: %d\n" , ret); |
664 | goto err_mac_unregister; |
665 | } |
666 | |
667 | return 0; |
668 | |
669 | err_mac_unregister: |
670 | ath12k_mac_unregister(ab); |
671 | |
672 | return ret; |
673 | } |
674 | |
675 | static void ath12k_core_pdev_destroy(struct ath12k_base *ab) |
676 | { |
677 | ath12k_mac_unregister(ab); |
678 | ath12k_hif_irq_disable(ab); |
679 | ath12k_dp_pdev_free(ab); |
680 | } |
681 | |
682 | static int ath12k_core_start(struct ath12k_base *ab, |
683 | enum ath12k_firmware_mode mode) |
684 | { |
685 | int ret; |
686 | |
687 | ret = ath12k_wmi_attach(ab); |
688 | if (ret) { |
689 | ath12k_err(ab, fmt: "failed to attach wmi: %d\n" , ret); |
690 | return ret; |
691 | } |
692 | |
693 | ret = ath12k_htc_init(ar: ab); |
694 | if (ret) { |
695 | ath12k_err(ab, fmt: "failed to init htc: %d\n" , ret); |
696 | goto err_wmi_detach; |
697 | } |
698 | |
699 | ret = ath12k_hif_start(ab); |
700 | if (ret) { |
701 | ath12k_err(ab, fmt: "failed to start HIF: %d\n" , ret); |
702 | goto err_wmi_detach; |
703 | } |
704 | |
705 | ret = ath12k_htc_wait_target(htc: &ab->htc); |
706 | if (ret) { |
707 | ath12k_err(ab, fmt: "failed to connect to HTC: %d\n" , ret); |
708 | goto err_hif_stop; |
709 | } |
710 | |
711 | ret = ath12k_dp_htt_connect(dp: &ab->dp); |
712 | if (ret) { |
713 | ath12k_err(ab, fmt: "failed to connect to HTT: %d\n" , ret); |
714 | goto err_hif_stop; |
715 | } |
716 | |
717 | ret = ath12k_wmi_connect(ab); |
718 | if (ret) { |
719 | ath12k_err(ab, fmt: "failed to connect wmi: %d\n" , ret); |
720 | goto err_hif_stop; |
721 | } |
722 | |
723 | ret = ath12k_htc_start(htc: &ab->htc); |
724 | if (ret) { |
725 | ath12k_err(ab, fmt: "failed to start HTC: %d\n" , ret); |
726 | goto err_hif_stop; |
727 | } |
728 | |
729 | ret = ath12k_wmi_wait_for_service_ready(ab); |
730 | if (ret) { |
731 | ath12k_err(ab, fmt: "failed to receive wmi service ready event: %d\n" , |
732 | ret); |
733 | goto err_hif_stop; |
734 | } |
735 | |
736 | ret = ath12k_mac_allocate(ab); |
737 | if (ret) { |
738 | ath12k_err(ab, fmt: "failed to create new hw device with mac80211 :%d\n" , |
739 | ret); |
740 | goto err_hif_stop; |
741 | } |
742 | |
743 | ath12k_dp_cc_config(ab); |
744 | |
745 | ret = ath12k_dp_rx_pdev_reo_setup(ab); |
746 | if (ret) { |
747 | ath12k_err(ab, fmt: "failed to initialize reo destination rings: %d\n" , ret); |
748 | goto err_mac_destroy; |
749 | } |
750 | |
751 | ath12k_dp_hal_rx_desc_init(ab); |
752 | |
753 | ret = ath12k_wmi_cmd_init(ab); |
754 | if (ret) { |
755 | ath12k_err(ab, fmt: "failed to send wmi init cmd: %d\n" , ret); |
756 | goto err_reo_cleanup; |
757 | } |
758 | |
759 | ret = ath12k_wmi_wait_for_unified_ready(ab); |
760 | if (ret) { |
761 | ath12k_err(ab, fmt: "failed to receive wmi unified ready event: %d\n" , |
762 | ret); |
763 | goto err_reo_cleanup; |
764 | } |
765 | |
766 | /* put hardware to DBS mode */ |
767 | if (ab->hw_params->single_pdev_only) { |
768 | ret = ath12k_wmi_set_hw_mode(ab, mode: WMI_HOST_HW_MODE_DBS); |
769 | if (ret) { |
770 | ath12k_err(ab, fmt: "failed to send dbs mode: %d\n" , ret); |
771 | goto err_reo_cleanup; |
772 | } |
773 | } |
774 | |
775 | ret = ath12k_dp_tx_htt_h2t_ver_req_msg(ab); |
776 | if (ret) { |
777 | ath12k_err(ab, fmt: "failed to send htt version request message: %d\n" , |
778 | ret); |
779 | goto err_reo_cleanup; |
780 | } |
781 | |
782 | return 0; |
783 | |
784 | err_reo_cleanup: |
785 | ath12k_dp_rx_pdev_reo_cleanup(ab); |
786 | err_mac_destroy: |
787 | ath12k_mac_destroy(ab); |
788 | err_hif_stop: |
789 | ath12k_hif_stop(ab); |
790 | err_wmi_detach: |
791 | ath12k_wmi_detach(ab); |
792 | return ret; |
793 | } |
794 | |
795 | static int ath12k_core_start_firmware(struct ath12k_base *ab, |
796 | enum ath12k_firmware_mode mode) |
797 | { |
798 | int ret; |
799 | |
800 | ath12k_ce_get_shadow_config(ab, shadow_cfg: &ab->qmi.ce_cfg.shadow_reg_v3, |
801 | shadow_cfg_len: &ab->qmi.ce_cfg.shadow_reg_v3_len); |
802 | |
803 | ret = ath12k_qmi_firmware_start(ab, mode); |
804 | if (ret) { |
805 | ath12k_err(ab, fmt: "failed to send firmware start: %d\n" , ret); |
806 | return ret; |
807 | } |
808 | |
809 | return ret; |
810 | } |
811 | |
812 | int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) |
813 | { |
814 | int ret; |
815 | |
816 | ret = ath12k_core_start_firmware(ab, mode: ATH12K_FIRMWARE_MODE_NORMAL); |
817 | if (ret) { |
818 | ath12k_err(ab, fmt: "failed to start firmware: %d\n" , ret); |
819 | return ret; |
820 | } |
821 | |
822 | ret = ath12k_ce_init_pipes(ab); |
823 | if (ret) { |
824 | ath12k_err(ab, fmt: "failed to initialize CE: %d\n" , ret); |
825 | goto err_firmware_stop; |
826 | } |
827 | |
828 | ret = ath12k_dp_alloc(ab); |
829 | if (ret) { |
830 | ath12k_err(ab, fmt: "failed to init DP: %d\n" , ret); |
831 | goto err_firmware_stop; |
832 | } |
833 | |
834 | mutex_lock(&ab->core_lock); |
835 | ret = ath12k_core_start(ab, mode: ATH12K_FIRMWARE_MODE_NORMAL); |
836 | if (ret) { |
837 | ath12k_err(ab, fmt: "failed to start core: %d\n" , ret); |
838 | goto err_dp_free; |
839 | } |
840 | |
841 | ret = ath12k_core_pdev_create(ab); |
842 | if (ret) { |
843 | ath12k_err(ab, fmt: "failed to create pdev core: %d\n" , ret); |
844 | goto err_core_stop; |
845 | } |
846 | ath12k_hif_irq_enable(ab); |
847 | |
848 | ret = ath12k_core_rfkill_config(ab); |
849 | if (ret && ret != -EOPNOTSUPP) { |
850 | ath12k_err(ab, fmt: "failed to config rfkill: %d\n" , ret); |
851 | goto err_core_pdev_destroy; |
852 | } |
853 | |
854 | mutex_unlock(lock: &ab->core_lock); |
855 | |
856 | return 0; |
857 | |
858 | err_core_pdev_destroy: |
859 | ath12k_core_pdev_destroy(ab); |
860 | err_core_stop: |
861 | ath12k_core_stop(ab); |
862 | ath12k_mac_destroy(ab); |
863 | err_dp_free: |
864 | ath12k_dp_free(ab); |
865 | mutex_unlock(lock: &ab->core_lock); |
866 | err_firmware_stop: |
867 | ath12k_qmi_firmware_stop(ab); |
868 | |
869 | return ret; |
870 | } |
871 | |
872 | static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab) |
873 | { |
874 | int ret; |
875 | |
876 | mutex_lock(&ab->core_lock); |
877 | ath12k_hif_irq_disable(ab); |
878 | ath12k_dp_pdev_free(ab); |
879 | ath12k_hif_stop(ab); |
880 | ath12k_wmi_detach(ab); |
881 | ath12k_dp_rx_pdev_reo_cleanup(ab); |
882 | mutex_unlock(lock: &ab->core_lock); |
883 | |
884 | ath12k_dp_free(ab); |
885 | ath12k_hal_srng_deinit(ath12k: ab); |
886 | |
887 | ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; |
888 | |
889 | ret = ath12k_hal_srng_init(ath12k: ab); |
890 | if (ret) |
891 | return ret; |
892 | |
893 | clear_bit(nr: ATH12K_FLAG_CRASH_FLUSH, addr: &ab->dev_flags); |
894 | |
895 | ret = ath12k_core_qmi_firmware_ready(ab); |
896 | if (ret) |
897 | goto err_hal_srng_deinit; |
898 | |
899 | clear_bit(nr: ATH12K_FLAG_RECOVERY, addr: &ab->dev_flags); |
900 | |
901 | return 0; |
902 | |
903 | err_hal_srng_deinit: |
904 | ath12k_hal_srng_deinit(ath12k: ab); |
905 | return ret; |
906 | } |
907 | |
908 | static void ath12k_rfkill_work(struct work_struct *work) |
909 | { |
910 | struct ath12k_base *ab = container_of(work, struct ath12k_base, rfkill_work); |
911 | struct ath12k *ar; |
912 | struct ath12k_hw *ah; |
913 | struct ieee80211_hw *hw; |
914 | bool rfkill_radio_on; |
915 | int i, j; |
916 | |
917 | spin_lock_bh(lock: &ab->base_lock); |
918 | rfkill_radio_on = ab->rfkill_radio_on; |
919 | spin_unlock_bh(lock: &ab->base_lock); |
920 | |
921 | for (i = 0; i < ab->num_hw; i++) { |
922 | ah = ab->ah[i]; |
923 | if (!ah) |
924 | continue; |
925 | |
926 | for (j = 0; j < ah->num_radio; j++) { |
927 | ar = &ah->radio[j]; |
928 | if (!ar) |
929 | continue; |
930 | |
931 | ath12k_mac_rfkill_enable_radio(ar, enable: rfkill_radio_on); |
932 | } |
933 | |
934 | hw = ah->hw; |
935 | wiphy_rfkill_set_hw_state(wiphy: hw->wiphy, blocked: !rfkill_radio_on); |
936 | } |
937 | } |
938 | |
939 | void ath12k_core_halt(struct ath12k *ar) |
940 | { |
941 | struct ath12k_base *ab = ar->ab; |
942 | |
943 | lockdep_assert_held(&ar->conf_mutex); |
944 | |
945 | ar->num_created_vdevs = 0; |
946 | ar->allocated_vdev_map = 0; |
947 | |
948 | ath12k_mac_scan_finish(ar); |
949 | ath12k_mac_peer_cleanup_all(ar); |
950 | cancel_delayed_work_sync(dwork: &ar->scan.timeout); |
951 | cancel_work_sync(work: &ar->regd_update_work); |
952 | cancel_work_sync(work: &ab->rfkill_work); |
953 | |
954 | rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); |
955 | synchronize_rcu(); |
956 | INIT_LIST_HEAD(list: &ar->arvifs); |
957 | idr_init(idr: &ar->txmgmt_idr); |
958 | } |
959 | |
960 | static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) |
961 | { |
962 | struct ath12k *ar; |
963 | struct ath12k_pdev *pdev; |
964 | struct ath12k_hw *ah; |
965 | int i; |
966 | |
967 | spin_lock_bh(lock: &ab->base_lock); |
968 | ab->stats.fw_crash_counter++; |
969 | spin_unlock_bh(lock: &ab->base_lock); |
970 | |
971 | if (ab->is_reset) |
972 | set_bit(nr: ATH12K_FLAG_CRASH_FLUSH, addr: &ab->dev_flags); |
973 | |
974 | for (i = 0; i < ab->num_hw; i++) { |
975 | if (!ab->ah[i]) |
976 | continue; |
977 | |
978 | ah = ab->ah[i]; |
979 | ieee80211_stop_queues(hw: ah->hw); |
980 | } |
981 | |
982 | for (i = 0; i < ab->num_radios; i++) { |
983 | pdev = &ab->pdevs[i]; |
984 | ar = pdev->ar; |
985 | if (!ar || ar->state == ATH12K_STATE_OFF) |
986 | continue; |
987 | |
988 | ath12k_mac_drain_tx(ar); |
989 | complete(&ar->scan.started); |
990 | complete(&ar->scan.completed); |
991 | complete(&ar->scan.on_channel); |
992 | complete(&ar->peer_assoc_done); |
993 | complete(&ar->peer_delete_done); |
994 | complete(&ar->install_key_done); |
995 | complete(&ar->vdev_setup_done); |
996 | complete(&ar->vdev_delete_done); |
997 | complete(&ar->bss_survey_done); |
998 | |
999 | wake_up(&ar->dp.tx_empty_waitq); |
1000 | idr_for_each(&ar->txmgmt_idr, |
1001 | fn: ath12k_mac_tx_mgmt_pending_free, data: ar); |
1002 | idr_destroy(&ar->txmgmt_idr); |
1003 | wake_up(&ar->txmgmt_empty_waitq); |
1004 | } |
1005 | |
1006 | wake_up(&ab->wmi_ab.tx_credits_wq); |
1007 | wake_up(&ab->peer_mapping_wq); |
1008 | } |
1009 | |
1010 | static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) |
1011 | { |
1012 | struct ath12k *ar; |
1013 | struct ath12k_pdev *pdev; |
1014 | int i; |
1015 | |
1016 | for (i = 0; i < ab->num_radios; i++) { |
1017 | pdev = &ab->pdevs[i]; |
1018 | ar = pdev->ar; |
1019 | if (!ar || ar->state == ATH12K_STATE_OFF) |
1020 | continue; |
1021 | |
1022 | mutex_lock(&ar->conf_mutex); |
1023 | |
1024 | switch (ar->state) { |
1025 | case ATH12K_STATE_ON: |
1026 | ar->state = ATH12K_STATE_RESTARTING; |
1027 | ath12k_core_halt(ar); |
1028 | ieee80211_restart_hw(hw: ath12k_ar_to_hw(ar)); |
1029 | break; |
1030 | case ATH12K_STATE_OFF: |
1031 | ath12k_warn(ab, |
1032 | fmt: "cannot restart radio %d that hasn't been started\n" , |
1033 | i); |
1034 | break; |
1035 | case ATH12K_STATE_RESTARTING: |
1036 | break; |
1037 | case ATH12K_STATE_RESTARTED: |
1038 | ar->state = ATH12K_STATE_WEDGED; |
1039 | fallthrough; |
1040 | case ATH12K_STATE_WEDGED: |
1041 | ath12k_warn(ab, |
1042 | fmt: "device is wedged, will not restart radio %d\n" , i); |
1043 | break; |
1044 | } |
1045 | mutex_unlock(lock: &ar->conf_mutex); |
1046 | } |
1047 | complete(&ab->driver_recovery); |
1048 | } |
1049 | |
1050 | static void ath12k_core_restart(struct work_struct *work) |
1051 | { |
1052 | struct ath12k_base *ab = container_of(work, struct ath12k_base, restart_work); |
1053 | int ret; |
1054 | |
1055 | if (!ab->is_reset) |
1056 | ath12k_core_pre_reconfigure_recovery(ab); |
1057 | |
1058 | ret = ath12k_core_reconfigure_on_crash(ab); |
1059 | if (ret) { |
1060 | ath12k_err(ab, fmt: "failed to reconfigure driver on crash recovery\n" ); |
1061 | return; |
1062 | } |
1063 | |
1064 | if (ab->is_reset) |
1065 | complete_all(&ab->reconfigure_complete); |
1066 | |
1067 | if (!ab->is_reset) |
1068 | ath12k_core_post_reconfigure_recovery(ab); |
1069 | } |
1070 | |
1071 | static void ath12k_core_reset(struct work_struct *work) |
1072 | { |
1073 | struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work); |
1074 | int reset_count, fail_cont_count; |
1075 | long time_left; |
1076 | |
1077 | if (!(test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags))) { |
1078 | ath12k_warn(ab, fmt: "ignore reset dev flags 0x%lx\n" , ab->dev_flags); |
1079 | return; |
1080 | } |
1081 | |
1082 | /* Sometimes the recovery will fail and then the next all recovery fail, |
1083 | * this is to avoid infinite recovery since it can not recovery success |
1084 | */ |
1085 | fail_cont_count = atomic_read(v: &ab->fail_cont_count); |
1086 | |
1087 | if (fail_cont_count >= ATH12K_RESET_MAX_FAIL_COUNT_FINAL) |
1088 | return; |
1089 | |
1090 | if (fail_cont_count >= ATH12K_RESET_MAX_FAIL_COUNT_FIRST && |
1091 | time_before(jiffies, ab->reset_fail_timeout)) |
1092 | return; |
1093 | |
1094 | reset_count = atomic_inc_return(v: &ab->reset_count); |
1095 | |
1096 | if (reset_count > 1) { |
1097 | /* Sometimes it happened another reset worker before the previous one |
1098 | * completed, then the second reset worker will destroy the previous one, |
1099 | * thus below is to avoid that. |
1100 | */ |
1101 | ath12k_warn(ab, fmt: "already resetting count %d\n" , reset_count); |
1102 | |
1103 | reinit_completion(x: &ab->reset_complete); |
1104 | time_left = wait_for_completion_timeout(x: &ab->reset_complete, |
1105 | ATH12K_RESET_TIMEOUT_HZ); |
1106 | if (time_left) { |
1107 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "to skip reset\n" ); |
1108 | atomic_dec(v: &ab->reset_count); |
1109 | return; |
1110 | } |
1111 | |
1112 | ab->reset_fail_timeout = jiffies + ATH12K_RESET_FAIL_TIMEOUT_HZ; |
1113 | /* Record the continuous recovery fail count when recovery failed*/ |
1114 | fail_cont_count = atomic_inc_return(v: &ab->fail_cont_count); |
1115 | } |
1116 | |
1117 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n" ); |
1118 | |
1119 | ab->is_reset = true; |
1120 | atomic_set(v: &ab->recovery_start_count, i: 0); |
1121 | reinit_completion(x: &ab->recovery_start); |
1122 | atomic_set(v: &ab->recovery_count, i: 0); |
1123 | |
1124 | ath12k_core_pre_reconfigure_recovery(ab); |
1125 | |
1126 | reinit_completion(x: &ab->reconfigure_complete); |
1127 | ath12k_core_post_reconfigure_recovery(ab); |
1128 | |
1129 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "waiting recovery start...\n" ); |
1130 | |
1131 | time_left = wait_for_completion_timeout(x: &ab->recovery_start, |
1132 | ATH12K_RECOVER_START_TIMEOUT_HZ); |
1133 | |
1134 | ath12k_hif_power_down(ab); |
1135 | ath12k_qmi_free_resource(ab); |
1136 | ath12k_hif_power_up(ab); |
1137 | |
1138 | ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n" ); |
1139 | } |
1140 | |
1141 | int ath12k_core_pre_init(struct ath12k_base *ab) |
1142 | { |
1143 | int ret; |
1144 | |
1145 | ret = ath12k_hw_init(ab); |
1146 | if (ret) { |
1147 | ath12k_err(ab, fmt: "failed to init hw params: %d\n" , ret); |
1148 | return ret; |
1149 | } |
1150 | |
1151 | ath12k_fw_map(ab); |
1152 | |
1153 | return 0; |
1154 | } |
1155 | |
1156 | int ath12k_core_init(struct ath12k_base *ab) |
1157 | { |
1158 | int ret; |
1159 | |
1160 | ret = ath12k_core_soc_create(ab); |
1161 | if (ret) { |
1162 | ath12k_err(ab, fmt: "failed to create soc core: %d\n" , ret); |
1163 | return ret; |
1164 | } |
1165 | |
1166 | return 0; |
1167 | } |
1168 | |
1169 | void ath12k_core_deinit(struct ath12k_base *ab) |
1170 | { |
1171 | mutex_lock(&ab->core_lock); |
1172 | |
1173 | ath12k_core_pdev_destroy(ab); |
1174 | ath12k_core_stop(ab); |
1175 | |
1176 | mutex_unlock(lock: &ab->core_lock); |
1177 | |
1178 | ath12k_hif_power_down(ab); |
1179 | ath12k_mac_destroy(ab); |
1180 | ath12k_core_soc_destroy(ab); |
1181 | ath12k_fw_unmap(ab); |
1182 | } |
1183 | |
1184 | void ath12k_core_free(struct ath12k_base *ab) |
1185 | { |
1186 | timer_delete_sync(timer: &ab->rx_replenish_retry); |
1187 | destroy_workqueue(wq: ab->workqueue_aux); |
1188 | destroy_workqueue(wq: ab->workqueue); |
1189 | kfree(objp: ab); |
1190 | } |
1191 | |
1192 | struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, |
1193 | enum ath12k_bus bus) |
1194 | { |
1195 | struct ath12k_base *ab; |
1196 | |
1197 | ab = kzalloc(size: sizeof(*ab) + priv_size, GFP_KERNEL); |
1198 | if (!ab) |
1199 | return NULL; |
1200 | |
1201 | init_completion(x: &ab->driver_recovery); |
1202 | |
1203 | ab->workqueue = create_singlethread_workqueue("ath12k_wq" ); |
1204 | if (!ab->workqueue) |
1205 | goto err_sc_free; |
1206 | |
1207 | ab->workqueue_aux = create_singlethread_workqueue("ath12k_aux_wq" ); |
1208 | if (!ab->workqueue_aux) |
1209 | goto err_free_wq; |
1210 | |
1211 | mutex_init(&ab->core_lock); |
1212 | spin_lock_init(&ab->base_lock); |
1213 | init_completion(x: &ab->reset_complete); |
1214 | init_completion(x: &ab->reconfigure_complete); |
1215 | init_completion(x: &ab->recovery_start); |
1216 | |
1217 | INIT_LIST_HEAD(list: &ab->peers); |
1218 | init_waitqueue_head(&ab->peer_mapping_wq); |
1219 | init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); |
1220 | INIT_WORK(&ab->restart_work, ath12k_core_restart); |
1221 | INIT_WORK(&ab->reset_work, ath12k_core_reset); |
1222 | INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work); |
1223 | |
1224 | timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0); |
1225 | init_completion(x: &ab->htc_suspend); |
1226 | |
1227 | ab->dev = dev; |
1228 | ab->hif.bus = bus; |
1229 | ab->qmi.num_radios = U8_MAX; |
1230 | ab->slo_capable = true; |
1231 | |
1232 | return ab; |
1233 | |
1234 | err_free_wq: |
1235 | destroy_workqueue(wq: ab->workqueue); |
1236 | err_sc_free: |
1237 | kfree(objp: ab); |
1238 | return NULL; |
1239 | } |
1240 | |
1241 | MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards." ); |
1242 | MODULE_LICENSE("Dual BSD/GPL" ); |
1243 | |