]>
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> | |
14f43c5f | 10 | #include <linux/of.h> |
d5c65159 KV |
11 | #include "core.h" |
12 | #include "dp_tx.h" | |
9c57d7e3 | 13 | #include "dp_rx.h" |
d5c65159 | 14 | #include "debug.h" |
31858805 | 15 | #include "hif.h" |
d5c65159 KV |
16 | |
17 | unsigned int ath11k_debug_mask; | |
6e0355af | 18 | EXPORT_SYMBOL(ath11k_debug_mask); |
d5c65159 KV |
19 | module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); |
20 | MODULE_PARM_DESC(debug_mask, "Debugging mask"); | |
21 | ||
aa2092a9 VN |
22 | static unsigned int ath11k_crypto_mode; |
23 | module_param_named(crypto_mode, ath11k_crypto_mode, uint, 0644); | |
24 | MODULE_PARM_DESC(crypto_mode, "crypto mode: 0-hardware, 1-software"); | |
25 | ||
26 | /* frame mode values are mapped as per enum ath11k_hw_txrx_mode */ | |
27 | unsigned int ath11k_frame_mode = ATH11K_HW_TXRX_NATIVE_WIFI; | |
28 | module_param_named(frame_mode, ath11k_frame_mode, uint, 0644); | |
29 | MODULE_PARM_DESC(frame_mode, | |
30 | "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)"); | |
31 | ||
d3318abf AK |
32 | static const struct ath11k_hw_params ath11k_hw_params[] = { |
33 | { | |
34 | .hw_rev = ATH11K_HW_IPQ8074, | |
35 | .name = "ipq8074 hw2.0", | |
36 | .fw = { | |
34d9fc80 | 37 | .dir = "IPQ8074/hw2.0", |
b3a18338 KV |
38 | .board_size = 256 * 1024, |
39 | .cal_size = 256 * 1024, | |
d3318abf | 40 | }, |
b1cc29e9 | 41 | .max_radios = 3, |
3b94ae4c | 42 | .bdf_addr = 0x4B0C0000, |
d547ca4c | 43 | .hw_ops = &ipq8074_ops, |
34d5a3a8 | 44 | .ring_mask = &ath11k_hw_ring_mask_ipq8074, |
727fae14 | 45 | .internal_sleep_clock = false, |
6976433c | 46 | .regs = &ipq8074_regs, |
e3396b8b CH |
47 | .host_ce_config = ath11k_host_ce_config_ipq8074, |
48 | .ce_count = 12, | |
967c1d11 AK |
49 | .target_ce_config = ath11k_target_ce_config_wlan_ipq8074, |
50 | .target_ce_count = 11, | |
51 | .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq8074, | |
52 | .svc_to_ce_map_len = 21, | |
5f859bc0 | 53 | .single_pdev_only = false, |
ed0192f7 | 54 | .needs_band_to_mac = true, |
7f6fc1eb | 55 | .rxdma1_enable = true, |
4152e420 CH |
56 | .num_rxmda_per_pdev = 1, |
57 | .rx_mac_buf_ring = false, | |
e7495035 | 58 | .vdev_start_delay = false, |
a6275302 | 59 | .htt_peer_map_v2 = true, |
065f5f68 | 60 | .tcl_0_only = false, |
5cca5fa1 | 61 | .spectral_fft_sz = 2, |
2626c269 KV |
62 | |
63 | .interface_modes = BIT(NL80211_IFTYPE_STATION) | | |
64 | BIT(NL80211_IFTYPE_AP) | | |
65 | BIT(NL80211_IFTYPE_MESH_POINT), | |
3f6e6c32 | 66 | .supports_monitor = true, |
e838c14a | 67 | .supports_shadow_regs = false, |
c83c500b | 68 | .idle_ps = false, |
02f9d3c1 | 69 | .cold_boot_calib = true, |
d5c65159 | 70 | }, |
b129699a AK |
71 | { |
72 | .hw_rev = ATH11K_HW_IPQ6018_HW10, | |
73 | .name = "ipq6018 hw1.0", | |
74 | .fw = { | |
75 | .dir = "IPQ6018/hw1.0", | |
76 | .board_size = 256 * 1024, | |
77 | .cal_size = 256 * 1024, | |
78 | }, | |
79 | .max_radios = 2, | |
80 | .bdf_addr = 0x4ABC0000, | |
81 | .hw_ops = &ipq6018_ops, | |
82 | .ring_mask = &ath11k_hw_ring_mask_ipq8074, | |
83 | .internal_sleep_clock = false, | |
84 | .regs = &ipq8074_regs, | |
85 | .host_ce_config = ath11k_host_ce_config_ipq8074, | |
86 | .ce_count = 12, | |
87 | .target_ce_config = ath11k_target_ce_config_wlan_ipq8074, | |
88 | .target_ce_count = 11, | |
89 | .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq6018, | |
90 | .svc_to_ce_map_len = 19, | |
91 | .single_pdev_only = false, | |
92 | .needs_band_to_mac = true, | |
93 | .rxdma1_enable = true, | |
94 | .num_rxmda_per_pdev = 1, | |
95 | .rx_mac_buf_ring = false, | |
96 | .vdev_start_delay = false, | |
97 | .htt_peer_map_v2 = true, | |
98 | .tcl_0_only = false, | |
5cca5fa1 | 99 | .spectral_fft_sz = 4, |
2626c269 KV |
100 | |
101 | .interface_modes = BIT(NL80211_IFTYPE_STATION) | | |
102 | BIT(NL80211_IFTYPE_AP) | | |
103 | BIT(NL80211_IFTYPE_MESH_POINT), | |
3f6e6c32 | 104 | .supports_monitor = true, |
e838c14a | 105 | .supports_shadow_regs = false, |
c83c500b | 106 | .idle_ps = false, |
02f9d3c1 | 107 | .cold_boot_calib = true, |
b129699a | 108 | }, |
9de2ad43 CH |
109 | { |
110 | .name = "qca6390 hw2.0", | |
111 | .hw_rev = ATH11K_HW_QCA6390_HW20, | |
112 | .fw = { | |
113 | .dir = "QCA6390/hw2.0", | |
114 | .board_size = 256 * 1024, | |
115 | .cal_size = 256 * 1024, | |
116 | }, | |
117 | .max_radios = 3, | |
118 | .bdf_addr = 0x4B0C0000, | |
119 | .hw_ops = &qca6390_ops, | |
d4ecb90b | 120 | .ring_mask = &ath11k_hw_ring_mask_qca6390, |
727fae14 | 121 | .internal_sleep_clock = true, |
6976433c | 122 | .regs = &qca6390_regs, |
e3396b8b CH |
123 | .host_ce_config = ath11k_host_ce_config_qca6390, |
124 | .ce_count = 9, | |
967c1d11 AK |
125 | .target_ce_config = ath11k_target_ce_config_wlan_qca6390, |
126 | .target_ce_count = 9, | |
127 | .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, | |
128 | .svc_to_ce_map_len = 14, | |
5f859bc0 | 129 | .single_pdev_only = true, |
ed0192f7 | 130 | .needs_band_to_mac = false, |
7f6fc1eb | 131 | .rxdma1_enable = false, |
4152e420 CH |
132 | .num_rxmda_per_pdev = 2, |
133 | .rx_mac_buf_ring = true, | |
e7495035 | 134 | .vdev_start_delay = true, |
a6275302 | 135 | .htt_peer_map_v2 = false, |
065f5f68 | 136 | .tcl_0_only = true, |
5cca5fa1 | 137 | .spectral_fft_sz = 0, |
2626c269 KV |
138 | |
139 | .interface_modes = BIT(NL80211_IFTYPE_STATION) | | |
140 | BIT(NL80211_IFTYPE_AP), | |
3f6e6c32 | 141 | .supports_monitor = false, |
e838c14a | 142 | .supports_shadow_regs = true, |
c83c500b | 143 | .idle_ps = true, |
02f9d3c1 | 144 | .cold_boot_calib = false, |
9de2ad43 | 145 | }, |
d5c65159 KV |
146 | }; |
147 | ||
14f43c5f SE |
148 | int ath11k_core_check_dt(struct ath11k_base *ab) |
149 | { | |
150 | size_t max_len = sizeof(ab->qmi.target.bdf_ext); | |
151 | const char *variant = NULL; | |
152 | struct device_node *node; | |
153 | ||
154 | node = ab->dev->of_node; | |
155 | if (!node) | |
156 | return -ENOENT; | |
157 | ||
158 | of_property_read_string(node, "qcom,ath11k-calibration-variant", | |
159 | &variant); | |
160 | if (!variant) | |
161 | return -ENODATA; | |
162 | ||
163 | if (strscpy(ab->qmi.target.bdf_ext, variant, max_len) < 0) | |
164 | ath11k_dbg(ab, ATH11K_DBG_BOOT, | |
165 | "bdf variant string is longer than the buffer can accommodate (variant: %s)\n", | |
166 | variant); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
d5c65159 KV |
171 | static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, |
172 | size_t name_len) | |
173 | { | |
14f43c5f SE |
174 | /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ |
175 | char variant[9 + ATH11K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; | |
176 | ||
177 | if (ab->qmi.target.bdf_ext[0] != '\0') | |
178 | scnprintf(variant, sizeof(variant), ",variant=%s", | |
179 | ab->qmi.target.bdf_ext); | |
180 | ||
d5c65159 | 181 | scnprintf(name, name_len, |
14f43c5f | 182 | "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", |
6eb6ea51 | 183 | ath11k_bus_str(ab->hif.bus), |
d5c65159 | 184 | ab->qmi.target.chip_id, |
14f43c5f | 185 | ab->qmi.target.board_id, variant); |
d5c65159 KV |
186 | |
187 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name); | |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
7b57b2dd | 192 | const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, |
7b57b2dd | 193 | const char *file) |
d5c65159 | 194 | { |
d5c65159 | 195 | const struct firmware *fw; |
34d9fc80 | 196 | char path[100]; |
d5c65159 KV |
197 | int ret; |
198 | ||
199 | if (file == NULL) | |
200 | return ERR_PTR(-ENOENT); | |
201 | ||
34d9fc80 | 202 | ath11k_core_create_firmware_path(ab, file, path, sizeof(path)); |
d5c65159 | 203 | |
34d9fc80 | 204 | ret = firmware_request_nowarn(&fw, path, ab->dev); |
d5c65159 KV |
205 | if (ret) |
206 | return ERR_PTR(ret); | |
7b57b2dd KV |
207 | |
208 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot firmware request %s size %zu\n", | |
34d9fc80 | 209 | path, fw->size); |
d5c65159 KV |
210 | |
211 | return fw; | |
212 | } | |
213 | ||
214 | void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) | |
215 | { | |
216 | if (!IS_ERR(bd->fw)) | |
217 | release_firmware(bd->fw); | |
218 | ||
219 | memset(bd, 0, sizeof(*bd)); | |
220 | } | |
221 | ||
222 | static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab, | |
223 | struct ath11k_board_data *bd, | |
224 | const void *buf, size_t buf_len, | |
225 | const char *boardname, | |
226 | int bd_ie_type) | |
227 | { | |
228 | const struct ath11k_fw_ie *hdr; | |
229 | bool name_match_found; | |
230 | int ret, board_ie_id; | |
231 | size_t board_ie_len; | |
232 | const void *board_ie_data; | |
233 | ||
234 | name_match_found = false; | |
235 | ||
236 | /* go through ATH11K_BD_IE_BOARD_ elements */ | |
237 | while (buf_len > sizeof(struct ath11k_fw_ie)) { | |
238 | hdr = buf; | |
239 | board_ie_id = le32_to_cpu(hdr->id); | |
240 | board_ie_len = le32_to_cpu(hdr->len); | |
241 | board_ie_data = hdr->data; | |
242 | ||
243 | buf_len -= sizeof(*hdr); | |
244 | buf += sizeof(*hdr); | |
245 | ||
246 | if (buf_len < ALIGN(board_ie_len, 4)) { | |
247 | ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", | |
248 | buf_len, ALIGN(board_ie_len, 4)); | |
249 | ret = -EINVAL; | |
250 | goto out; | |
251 | } | |
252 | ||
253 | switch (board_ie_id) { | |
254 | case ATH11K_BD_IE_BOARD_NAME: | |
255 | ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", | |
256 | board_ie_data, board_ie_len); | |
257 | ||
258 | if (board_ie_len != strlen(boardname)) | |
259 | break; | |
260 | ||
261 | ret = memcmp(board_ie_data, boardname, strlen(boardname)); | |
262 | if (ret) | |
263 | break; | |
264 | ||
265 | name_match_found = true; | |
266 | ath11k_dbg(ab, ATH11K_DBG_BOOT, | |
267 | "boot found match for name '%s'", | |
268 | boardname); | |
269 | break; | |
270 | case ATH11K_BD_IE_BOARD_DATA: | |
271 | if (!name_match_found) | |
272 | /* no match found */ | |
273 | break; | |
274 | ||
275 | ath11k_dbg(ab, ATH11K_DBG_BOOT, | |
276 | "boot found board data for '%s'", boardname); | |
277 | ||
278 | bd->data = board_ie_data; | |
279 | bd->len = board_ie_len; | |
280 | ||
281 | ret = 0; | |
282 | goto out; | |
283 | default: | |
284 | ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", | |
285 | board_ie_id); | |
286 | break; | |
287 | } | |
288 | ||
289 | /* jump over the padding */ | |
290 | board_ie_len = ALIGN(board_ie_len, 4); | |
291 | ||
292 | buf_len -= board_ie_len; | |
293 | buf += board_ie_len; | |
294 | } | |
295 | ||
296 | /* no match found */ | |
297 | ret = -ENOENT; | |
298 | ||
299 | out: | |
300 | return ret; | |
301 | } | |
302 | ||
303 | static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, | |
304 | struct ath11k_board_data *bd, | |
305 | const char *boardname) | |
306 | { | |
307 | size_t len, magic_len; | |
308 | const u8 *data; | |
31d78a3d | 309 | char *filename, filepath[100]; |
d5c65159 KV |
310 | size_t ie_len; |
311 | struct ath11k_fw_ie *hdr; | |
312 | int ret, ie_id; | |
313 | ||
31d78a3d KV |
314 | filename = ATH11K_BOARD_API2_FILE; |
315 | ||
d5c65159 | 316 | if (!bd->fw) |
34d9fc80 KV |
317 | bd->fw = ath11k_core_firmware_request(ab, filename); |
318 | ||
d5c65159 KV |
319 | if (IS_ERR(bd->fw)) |
320 | return PTR_ERR(bd->fw); | |
321 | ||
322 | data = bd->fw->data; | |
323 | len = bd->fw->size; | |
324 | ||
31d78a3d KV |
325 | ath11k_core_create_firmware_path(ab, filename, |
326 | filepath, sizeof(filepath)); | |
327 | ||
d5c65159 KV |
328 | /* magic has extra null byte padded */ |
329 | magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; | |
330 | if (len < magic_len) { | |
31d78a3d KV |
331 | ath11k_err(ab, "failed to find magic value in %s, file too short: %zu\n", |
332 | filepath, len); | |
d5c65159 KV |
333 | ret = -EINVAL; |
334 | goto err; | |
335 | } | |
336 | ||
337 | if (memcmp(data, ATH11K_BOARD_MAGIC, magic_len)) { | |
338 | ath11k_err(ab, "found invalid board magic\n"); | |
339 | ret = -EINVAL; | |
340 | goto err; | |
341 | } | |
342 | ||
343 | /* magic is padded to 4 bytes */ | |
344 | magic_len = ALIGN(magic_len, 4); | |
345 | if (len < magic_len) { | |
31d78a3d KV |
346 | ath11k_err(ab, "failed: %s too small to contain board data, len: %zu\n", |
347 | filepath, len); | |
d5c65159 KV |
348 | ret = -EINVAL; |
349 | goto err; | |
350 | } | |
351 | ||
352 | data += magic_len; | |
353 | len -= magic_len; | |
354 | ||
355 | while (len > sizeof(struct ath11k_fw_ie)) { | |
356 | hdr = (struct ath11k_fw_ie *)data; | |
357 | ie_id = le32_to_cpu(hdr->id); | |
358 | ie_len = le32_to_cpu(hdr->len); | |
359 | ||
360 | len -= sizeof(*hdr); | |
361 | data = hdr->data; | |
362 | ||
363 | if (len < ALIGN(ie_len, 4)) { | |
364 | ath11k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n", | |
365 | ie_id, ie_len, len); | |
366 | return -EINVAL; | |
367 | } | |
368 | ||
369 | switch (ie_id) { | |
370 | case ATH11K_BD_IE_BOARD: | |
371 | ret = ath11k_core_parse_bd_ie_board(ab, bd, data, | |
372 | ie_len, | |
373 | boardname, | |
374 | ATH11K_BD_IE_BOARD); | |
375 | if (ret == -ENOENT) | |
376 | /* no match found, continue */ | |
377 | break; | |
378 | else if (ret) | |
379 | /* there was an error, bail out */ | |
380 | goto err; | |
381 | /* either found or error, so stop searching */ | |
382 | goto out; | |
383 | } | |
384 | ||
385 | /* jump over the padding */ | |
386 | ie_len = ALIGN(ie_len, 4); | |
387 | ||
388 | len -= ie_len; | |
389 | data += ie_len; | |
390 | } | |
391 | ||
392 | out: | |
393 | if (!bd->data || !bd->len) { | |
394 | ath11k_err(ab, | |
31d78a3d KV |
395 | "failed to fetch board data for %s from %s\n", |
396 | boardname, filepath); | |
d5c65159 KV |
397 | ret = -ENODATA; |
398 | goto err; | |
399 | } | |
400 | ||
401 | return 0; | |
402 | ||
403 | err: | |
404 | ath11k_core_free_bdf(ab, bd); | |
405 | return ret; | |
406 | } | |
407 | ||
408 | static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, | |
409 | struct ath11k_board_data *bd) | |
410 | { | |
34d9fc80 | 411 | bd->fw = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_BOARD_FILE); |
d5c65159 KV |
412 | if (IS_ERR(bd->fw)) |
413 | return PTR_ERR(bd->fw); | |
414 | ||
415 | bd->data = bd->fw->data; | |
416 | bd->len = bd->fw->size; | |
417 | ||
418 | return 0; | |
419 | } | |
420 | ||
421 | #define BOARD_NAME_SIZE 100 | |
422 | int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) | |
423 | { | |
424 | char boardname[BOARD_NAME_SIZE]; | |
425 | int ret; | |
426 | ||
427 | ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); | |
428 | if (ret) { | |
429 | ath11k_err(ab, "failed to create board name: %d", ret); | |
430 | return ret; | |
431 | } | |
432 | ||
433 | ab->bd_api = 2; | |
434 | ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); | |
435 | if (!ret) | |
436 | goto success; | |
437 | ||
438 | ab->bd_api = 1; | |
439 | ret = ath11k_core_fetch_board_data_api_1(ab, bd); | |
440 | if (ret) { | |
441 | ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", | |
442 | ab->hw_params.fw.dir); | |
443 | return ret; | |
444 | } | |
445 | ||
446 | success: | |
447 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "using board api %d\n", ab->bd_api); | |
448 | return 0; | |
449 | } | |
450 | ||
451 | static void ath11k_core_stop(struct ath11k_base *ab) | |
452 | { | |
453 | if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) | |
454 | ath11k_qmi_firmware_stop(ab); | |
e838c14a | 455 | |
31858805 | 456 | ath11k_hif_stop(ab); |
d5c65159 | 457 | ath11k_wmi_detach(ab); |
9c57d7e3 | 458 | ath11k_dp_pdev_reo_cleanup(ab); |
d5c65159 KV |
459 | |
460 | /* De-Init of components as needed */ | |
461 | } | |
462 | ||
463 | static int ath11k_core_soc_create(struct ath11k_base *ab) | |
464 | { | |
465 | int ret; | |
466 | ||
467 | ret = ath11k_qmi_init_service(ab); | |
468 | if (ret) { | |
469 | ath11k_err(ab, "failed to initialize qmi :%d\n", ret); | |
470 | return ret; | |
471 | } | |
472 | ||
cb4e57db | 473 | ret = ath11k_debugfs_soc_create(ab); |
d5c65159 KV |
474 | if (ret) { |
475 | ath11k_err(ab, "failed to create ath11k debugfs\n"); | |
476 | goto err_qmi_deinit; | |
477 | } | |
478 | ||
31858805 | 479 | ret = ath11k_hif_power_up(ab); |
d5c65159 KV |
480 | if (ret) { |
481 | ath11k_err(ab, "failed to power up :%d\n", ret); | |
482 | goto err_debugfs_reg; | |
483 | } | |
484 | ||
485 | return 0; | |
486 | ||
487 | err_debugfs_reg: | |
cb4e57db | 488 | ath11k_debugfs_soc_destroy(ab); |
d5c65159 KV |
489 | err_qmi_deinit: |
490 | ath11k_qmi_deinit_service(ab); | |
491 | return ret; | |
492 | } | |
493 | ||
494 | static void ath11k_core_soc_destroy(struct ath11k_base *ab) | |
495 | { | |
cb4e57db | 496 | ath11k_debugfs_soc_destroy(ab); |
d5c65159 KV |
497 | ath11k_dp_free(ab); |
498 | ath11k_reg_free(ab); | |
499 | ath11k_qmi_deinit_service(ab); | |
500 | } | |
501 | ||
502 | static int ath11k_core_pdev_create(struct ath11k_base *ab) | |
503 | { | |
504 | int ret; | |
505 | ||
cb4e57db | 506 | ret = ath11k_debugfs_pdev_create(ab); |
d5c65159 KV |
507 | if (ret) { |
508 | ath11k_err(ab, "failed to create core pdev debugfs: %d\n", ret); | |
509 | return ret; | |
510 | } | |
511 | ||
0366f426 | 512 | ret = ath11k_mac_register(ab); |
d5c65159 | 513 | if (ret) { |
0366f426 | 514 | ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); |
d5c65159 KV |
515 | goto err_pdev_debug; |
516 | } | |
517 | ||
518 | ret = ath11k_dp_pdev_alloc(ab); | |
519 | if (ret) { | |
520 | ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); | |
0366f426 | 521 | goto err_mac_unregister; |
d5c65159 KV |
522 | } |
523 | ||
2a63bbca PKC |
524 | ret = ath11k_thermal_register(ab); |
525 | if (ret) { | |
526 | ath11k_err(ab, "could not register thermal device: %d\n", | |
527 | ret); | |
528 | goto err_dp_pdev_free; | |
529 | } | |
530 | ||
9d11b7bf KP |
531 | ret = ath11k_spectral_init(ab); |
532 | if (ret) { | |
533 | ath11k_err(ab, "failed to init spectral %d\n", ret); | |
534 | goto err_thermal_unregister; | |
535 | } | |
536 | ||
d5c65159 KV |
537 | return 0; |
538 | ||
9d11b7bf KP |
539 | err_thermal_unregister: |
540 | ath11k_thermal_unregister(ab); | |
2a63bbca PKC |
541 | err_dp_pdev_free: |
542 | ath11k_dp_pdev_free(ab); | |
0366f426 VT |
543 | err_mac_unregister: |
544 | ath11k_mac_unregister(ab); | |
d5c65159 | 545 | err_pdev_debug: |
cb4e57db | 546 | ath11k_debugfs_pdev_destroy(ab); |
d5c65159 KV |
547 | |
548 | return ret; | |
549 | } | |
550 | ||
551 | static void ath11k_core_pdev_destroy(struct ath11k_base *ab) | |
552 | { | |
9d11b7bf | 553 | ath11k_spectral_deinit(ab); |
2a63bbca | 554 | ath11k_thermal_unregister(ab); |
d5c65159 | 555 | ath11k_mac_unregister(ab); |
31858805 | 556 | ath11k_hif_irq_disable(ab); |
d5c65159 | 557 | ath11k_dp_pdev_free(ab); |
cb4e57db | 558 | ath11k_debugfs_pdev_destroy(ab); |
d5c65159 KV |
559 | } |
560 | ||
561 | static int ath11k_core_start(struct ath11k_base *ab, | |
562 | enum ath11k_firmware_mode mode) | |
563 | { | |
564 | int ret; | |
565 | ||
566 | ret = ath11k_qmi_firmware_start(ab, mode); | |
567 | if (ret) { | |
568 | ath11k_err(ab, "failed to attach wmi: %d\n", ret); | |
569 | return ret; | |
570 | } | |
571 | ||
572 | ret = ath11k_wmi_attach(ab); | |
573 | if (ret) { | |
574 | ath11k_err(ab, "failed to attach wmi: %d\n", ret); | |
575 | goto err_firmware_stop; | |
576 | } | |
577 | ||
578 | ret = ath11k_htc_init(ab); | |
579 | if (ret) { | |
580 | ath11k_err(ab, "failed to init htc: %d\n", ret); | |
581 | goto err_wmi_detach; | |
582 | } | |
583 | ||
31858805 | 584 | ret = ath11k_hif_start(ab); |
d5c65159 KV |
585 | if (ret) { |
586 | ath11k_err(ab, "failed to start HIF: %d\n", ret); | |
587 | goto err_wmi_detach; | |
588 | } | |
589 | ||
590 | ret = ath11k_htc_wait_target(&ab->htc); | |
591 | if (ret) { | |
592 | ath11k_err(ab, "failed to connect to HTC: %d\n", ret); | |
593 | goto err_hif_stop; | |
594 | } | |
595 | ||
596 | ret = ath11k_dp_htt_connect(&ab->dp); | |
597 | if (ret) { | |
598 | ath11k_err(ab, "failed to connect to HTT: %d\n", ret); | |
599 | goto err_hif_stop; | |
600 | } | |
601 | ||
602 | ret = ath11k_wmi_connect(ab); | |
603 | if (ret) { | |
604 | ath11k_err(ab, "failed to connect wmi: %d\n", ret); | |
605 | goto err_hif_stop; | |
606 | } | |
607 | ||
608 | ret = ath11k_htc_start(&ab->htc); | |
609 | if (ret) { | |
610 | ath11k_err(ab, "failed to start HTC: %d\n", ret); | |
611 | goto err_hif_stop; | |
612 | } | |
613 | ||
614 | ret = ath11k_wmi_wait_for_service_ready(ab); | |
615 | if (ret) { | |
616 | ath11k_err(ab, "failed to receive wmi service ready event: %d\n", | |
617 | ret); | |
618 | goto err_hif_stop; | |
619 | } | |
620 | ||
0366f426 VT |
621 | ret = ath11k_mac_allocate(ab); |
622 | if (ret) { | |
623 | ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", | |
624 | ret); | |
625 | goto err_hif_stop; | |
626 | } | |
627 | ||
9c57d7e3 VT |
628 | ath11k_dp_pdev_pre_alloc(ab); |
629 | ||
630 | ret = ath11k_dp_pdev_reo_setup(ab); | |
631 | if (ret) { | |
632 | ath11k_err(ab, "failed to initialize reo destination rings: %d\n", ret); | |
633 | goto err_mac_destroy; | |
634 | } | |
635 | ||
d5c65159 KV |
636 | ret = ath11k_wmi_cmd_init(ab); |
637 | if (ret) { | |
638 | ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); | |
9c57d7e3 | 639 | goto err_reo_cleanup; |
d5c65159 KV |
640 | } |
641 | ||
642 | ret = ath11k_wmi_wait_for_unified_ready(ab); | |
643 | if (ret) { | |
644 | ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", | |
645 | ret); | |
9c57d7e3 | 646 | goto err_reo_cleanup; |
d5c65159 KV |
647 | } |
648 | ||
649 | ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); | |
650 | if (ret) { | |
651 | ath11k_err(ab, "failed to send htt version request message: %d\n", | |
652 | ret); | |
9c57d7e3 | 653 | goto err_reo_cleanup; |
d5c65159 KV |
654 | } |
655 | ||
656 | return 0; | |
657 | ||
9c57d7e3 VT |
658 | err_reo_cleanup: |
659 | ath11k_dp_pdev_reo_cleanup(ab); | |
0366f426 VT |
660 | err_mac_destroy: |
661 | ath11k_mac_destroy(ab); | |
d5c65159 | 662 | err_hif_stop: |
31858805 | 663 | ath11k_hif_stop(ab); |
d5c65159 KV |
664 | err_wmi_detach: |
665 | ath11k_wmi_detach(ab); | |
666 | err_firmware_stop: | |
667 | ath11k_qmi_firmware_stop(ab); | |
668 | ||
669 | return ret; | |
670 | } | |
671 | ||
672 | int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) | |
673 | { | |
674 | int ret; | |
675 | ||
676 | ret = ath11k_ce_init_pipes(ab); | |
677 | if (ret) { | |
678 | ath11k_err(ab, "failed to initialize CE: %d\n", ret); | |
679 | return ret; | |
680 | } | |
681 | ||
682 | ret = ath11k_dp_alloc(ab); | |
683 | if (ret) { | |
684 | ath11k_err(ab, "failed to init DP: %d\n", ret); | |
685 | return ret; | |
686 | } | |
687 | ||
aa2092a9 VN |
688 | switch (ath11k_crypto_mode) { |
689 | case ATH11K_CRYPT_MODE_SW: | |
690 | set_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); | |
691 | set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); | |
692 | break; | |
693 | case ATH11K_CRYPT_MODE_HW: | |
694 | clear_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags); | |
695 | clear_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); | |
696 | break; | |
697 | default: | |
698 | ath11k_info(ab, "invalid crypto_mode: %d\n", ath11k_crypto_mode); | |
699 | return -EINVAL; | |
700 | } | |
701 | ||
702 | if (ath11k_frame_mode == ATH11K_HW_TXRX_RAW) | |
703 | set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags); | |
704 | ||
d5c65159 KV |
705 | mutex_lock(&ab->core_lock); |
706 | ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); | |
707 | if (ret) { | |
708 | ath11k_err(ab, "failed to start core: %d\n", ret); | |
709 | goto err_dp_free; | |
710 | } | |
711 | ||
712 | ret = ath11k_core_pdev_create(ab); | |
713 | if (ret) { | |
714 | ath11k_err(ab, "failed to create pdev core: %d\n", ret); | |
715 | goto err_core_stop; | |
716 | } | |
31858805 | 717 | ath11k_hif_irq_enable(ab); |
d5c65159 KV |
718 | mutex_unlock(&ab->core_lock); |
719 | ||
720 | return 0; | |
721 | ||
722 | err_core_stop: | |
723 | ath11k_core_stop(ab); | |
0366f426 | 724 | ath11k_mac_destroy(ab); |
d5c65159 KV |
725 | err_dp_free: |
726 | ath11k_dp_free(ab); | |
ba479239 | 727 | mutex_unlock(&ab->core_lock); |
d5c65159 KV |
728 | return ret; |
729 | } | |
730 | ||
731 | static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) | |
732 | { | |
733 | int ret; | |
734 | ||
735 | mutex_lock(&ab->core_lock); | |
2a63bbca | 736 | ath11k_thermal_unregister(ab); |
31858805 | 737 | ath11k_hif_irq_disable(ab); |
d5c65159 | 738 | ath11k_dp_pdev_free(ab); |
9d11b7bf | 739 | ath11k_spectral_deinit(ab); |
31858805 | 740 | ath11k_hif_stop(ab); |
d5c65159 | 741 | ath11k_wmi_detach(ab); |
9c57d7e3 | 742 | ath11k_dp_pdev_reo_cleanup(ab); |
d5c65159 KV |
743 | mutex_unlock(&ab->core_lock); |
744 | ||
745 | ath11k_dp_free(ab); | |
746 | ath11k_hal_srng_deinit(ab); | |
747 | ||
748 | ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; | |
749 | ||
750 | ret = ath11k_hal_srng_init(ab); | |
751 | if (ret) | |
752 | return ret; | |
753 | ||
754 | clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); | |
755 | ||
756 | ret = ath11k_core_qmi_firmware_ready(ab); | |
757 | if (ret) | |
758 | goto err_hal_srng_deinit; | |
759 | ||
760 | clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); | |
761 | ||
762 | return 0; | |
763 | ||
764 | err_hal_srng_deinit: | |
765 | ath11k_hal_srng_deinit(ab); | |
766 | return ret; | |
767 | } | |
768 | ||
769 | void ath11k_core_halt(struct ath11k *ar) | |
770 | { | |
771 | struct ath11k_base *ab = ar->ab; | |
772 | ||
773 | lockdep_assert_held(&ar->conf_mutex); | |
774 | ||
775 | ar->num_created_vdevs = 0; | |
79c080db | 776 | ar->allocated_vdev_map = 0; |
d5c65159 KV |
777 | |
778 | ath11k_mac_scan_finish(ar); | |
779 | ath11k_mac_peer_cleanup_all(ar); | |
780 | cancel_delayed_work_sync(&ar->scan.timeout); | |
781 | cancel_work_sync(&ar->regd_update_work); | |
782 | ||
783 | rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); | |
784 | synchronize_rcu(); | |
785 | INIT_LIST_HEAD(&ar->arvifs); | |
786 | idr_init(&ar->txmgmt_idr); | |
787 | } | |
788 | ||
789 | static void ath11k_core_restart(struct work_struct *work) | |
790 | { | |
791 | struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); | |
792 | struct ath11k *ar; | |
793 | struct ath11k_pdev *pdev; | |
794 | int i, ret = 0; | |
795 | ||
796 | spin_lock_bh(&ab->base_lock); | |
797 | ab->stats.fw_crash_counter++; | |
798 | spin_unlock_bh(&ab->base_lock); | |
799 | ||
800 | for (i = 0; i < ab->num_radios; i++) { | |
801 | pdev = &ab->pdevs[i]; | |
802 | ar = pdev->ar; | |
803 | if (!ar || ar->state == ATH11K_STATE_OFF) | |
804 | continue; | |
805 | ||
806 | ieee80211_stop_queues(ar->hw); | |
807 | ath11k_mac_drain_tx(ar); | |
808 | complete(&ar->scan.started); | |
809 | complete(&ar->scan.completed); | |
810 | complete(&ar->peer_assoc_done); | |
811 | complete(&ar->install_key_done); | |
812 | complete(&ar->vdev_setup_done); | |
813 | complete(&ar->bss_survey_done); | |
a41d1034 | 814 | complete(&ar->thermal.wmi_sync); |
d5c65159 KV |
815 | |
816 | wake_up(&ar->dp.tx_empty_waitq); | |
817 | idr_for_each(&ar->txmgmt_idr, | |
818 | ath11k_mac_tx_mgmt_pending_free, ar); | |
819 | idr_destroy(&ar->txmgmt_idr); | |
820 | } | |
821 | ||
6bc9d6f7 | 822 | wake_up(&ab->wmi_ab.tx_credits_wq); |
d5c65159 KV |
823 | wake_up(&ab->peer_mapping_wq); |
824 | ||
825 | ret = ath11k_core_reconfigure_on_crash(ab); | |
826 | if (ret) { | |
827 | ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); | |
828 | return; | |
829 | } | |
830 | ||
831 | for (i = 0; i < ab->num_radios; i++) { | |
832 | pdev = &ab->pdevs[i]; | |
833 | ar = pdev->ar; | |
834 | if (!ar || ar->state == ATH11K_STATE_OFF) | |
835 | continue; | |
836 | ||
837 | mutex_lock(&ar->conf_mutex); | |
838 | ||
839 | switch (ar->state) { | |
840 | case ATH11K_STATE_ON: | |
841 | ar->state = ATH11K_STATE_RESTARTING; | |
842 | ath11k_core_halt(ar); | |
843 | ieee80211_restart_hw(ar->hw); | |
844 | break; | |
845 | case ATH11K_STATE_OFF: | |
846 | ath11k_warn(ab, | |
847 | "cannot restart radio %d that hasn't been started\n", | |
848 | i); | |
849 | break; | |
850 | case ATH11K_STATE_RESTARTING: | |
851 | break; | |
852 | case ATH11K_STATE_RESTARTED: | |
853 | ar->state = ATH11K_STATE_WEDGED; | |
0b294aeb | 854 | fallthrough; |
d5c65159 KV |
855 | case ATH11K_STATE_WEDGED: |
856 | ath11k_warn(ab, | |
857 | "device is wedged, will not restart radio %d\n", i); | |
858 | break; | |
859 | } | |
860 | mutex_unlock(&ar->conf_mutex); | |
861 | } | |
862 | complete(&ab->driver_recovery); | |
863 | } | |
864 | ||
d3318abf AK |
865 | static int ath11k_init_hw_params(struct ath11k_base *ab) |
866 | { | |
867 | const struct ath11k_hw_params *hw_params = NULL; | |
868 | int i; | |
869 | ||
870 | for (i = 0; i < ARRAY_SIZE(ath11k_hw_params); i++) { | |
871 | hw_params = &ath11k_hw_params[i]; | |
872 | ||
873 | if (hw_params->hw_rev == ab->hw_rev) | |
874 | break; | |
875 | } | |
876 | ||
877 | if (i == ARRAY_SIZE(ath11k_hw_params)) { | |
878 | ath11k_err(ab, "Unsupported hardware version: 0x%x\n", ab->hw_rev); | |
879 | return -EINVAL; | |
880 | } | |
881 | ||
882 | ab->hw_params = *hw_params; | |
883 | ||
884 | ath11k_dbg(ab, ATH11K_DBG_BOOT, "Hardware name %s\n", ab->hw_params.name); | |
885 | ||
886 | return 0; | |
887 | } | |
888 | ||
b8246f88 KV |
889 | int ath11k_core_pre_init(struct ath11k_base *ab) |
890 | { | |
891 | int ret; | |
892 | ||
893 | ret = ath11k_init_hw_params(ab); | |
894 | if (ret) { | |
895 | ath11k_err(ab, "failed to get hw params: %d\n", ret); | |
896 | return ret; | |
897 | } | |
898 | ||
899 | return 0; | |
900 | } | |
901 | EXPORT_SYMBOL(ath11k_core_pre_init); | |
902 | ||
1ff8ed78 GS |
903 | int ath11k_core_init(struct ath11k_base *ab) |
904 | { | |
905 | int ret; | |
906 | ||
d5c65159 KV |
907 | ret = ath11k_core_soc_create(ab); |
908 | if (ret) { | |
909 | ath11k_err(ab, "failed to create soc core: %d\n", ret); | |
910 | return ret; | |
911 | } | |
912 | ||
913 | return 0; | |
914 | } | |
7f4beda2 | 915 | EXPORT_SYMBOL(ath11k_core_init); |
d5c65159 KV |
916 | |
917 | void ath11k_core_deinit(struct ath11k_base *ab) | |
918 | { | |
919 | mutex_lock(&ab->core_lock); | |
920 | ||
921 | ath11k_core_pdev_destroy(ab); | |
922 | ath11k_core_stop(ab); | |
923 | ||
924 | mutex_unlock(&ab->core_lock); | |
925 | ||
31858805 | 926 | ath11k_hif_power_down(ab); |
d5c65159 KV |
927 | ath11k_mac_destroy(ab); |
928 | ath11k_core_soc_destroy(ab); | |
929 | } | |
6e0355af | 930 | EXPORT_SYMBOL(ath11k_core_deinit); |
d5c65159 KV |
931 | |
932 | void ath11k_core_free(struct ath11k_base *ab) | |
933 | { | |
934 | kfree(ab); | |
935 | } | |
6e0355af | 936 | EXPORT_SYMBOL(ath11k_core_free); |
d5c65159 | 937 | |
630ad41c | 938 | struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, |
1ff8ed78 GS |
939 | enum ath11k_bus bus, |
940 | const struct ath11k_bus_params *bus_params) | |
d5c65159 KV |
941 | { |
942 | struct ath11k_base *ab; | |
943 | ||
630ad41c | 944 | ab = kzalloc(sizeof(*ab) + priv_size, GFP_KERNEL); |
d5c65159 KV |
945 | if (!ab) |
946 | return NULL; | |
947 | ||
948 | init_completion(&ab->driver_recovery); | |
949 | ||
950 | ab->workqueue = create_singlethread_workqueue("ath11k_wq"); | |
951 | if (!ab->workqueue) | |
952 | goto err_sc_free; | |
953 | ||
954 | mutex_init(&ab->core_lock); | |
955 | spin_lock_init(&ab->base_lock); | |
956 | ||
957 | INIT_LIST_HEAD(&ab->peers); | |
958 | init_waitqueue_head(&ab->peer_mapping_wq); | |
6bc9d6f7 | 959 | init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); |
02f9d3c1 | 960 | init_waitqueue_head(&ab->qmi.cold_boot_waitq); |
d5c65159 KV |
961 | INIT_WORK(&ab->restart_work, ath11k_core_restart); |
962 | timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); | |
963 | ab->dev = dev; | |
1ff8ed78 | 964 | ab->bus_params = *bus_params; |
6eb6ea51 | 965 | ab->hif.bus = bus; |
d5c65159 KV |
966 | |
967 | return ab; | |
968 | ||
969 | err_sc_free: | |
970 | kfree(ab); | |
971 | return NULL; | |
972 | } | |
6e0355af GS |
973 | EXPORT_SYMBOL(ath11k_core_alloc); |
974 | ||
975 | MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11ax wireless LAN cards."); | |
976 | MODULE_LICENSE("Dual BSD/GPL"); |