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. | |
92c1858e | 4 | * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. |
d5c65159 KV |
5 | */ |
6 | ||
7 | #include <linux/module.h> | |
8 | #include <linux/platform_device.h> | |
9 | #include <linux/of_device.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/dma-mapping.h> | |
f9eec494 MP |
12 | #include <linux/of_address.h> |
13 | #include <linux/iommu.h> | |
d5c65159 KV |
14 | #include "ahb.h" |
15 | #include "debug.h" | |
31858805 | 16 | #include "hif.h" |
d5c65159 | 17 | #include <linux/remoteproc.h> |
00402f49 | 18 | #include "pcic.h" |
d5c65159 KV |
19 | |
20 | static const struct of_device_id ath11k_ahb_of_match[] = { | |
21 | /* TODO: Should we change the compatible string to something similar | |
22 | * to one that ath10k uses? | |
23 | */ | |
24 | { .compatible = "qcom,ipq8074-wifi", | |
25 | .data = (void *)ATH11K_HW_IPQ8074, | |
26 | }, | |
b129699a AK |
27 | { .compatible = "qcom,ipq6018-wifi", |
28 | .data = (void *)ATH11K_HW_IPQ6018_HW10, | |
29 | }, | |
00402f49 MP |
30 | { .compatible = "qcom,wcn6750-wifi", |
31 | .data = (void *)ATH11K_HW_WCN6750_HW10, | |
32 | }, | |
d5c65159 KV |
33 | { } |
34 | }; | |
35 | ||
36 | MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); | |
37 | ||
d5c65159 KV |
38 | #define ATH11K_IRQ_CE0_OFFSET 4 |
39 | ||
40 | static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { | |
41 | "misc-pulse1", | |
42 | "misc-latch", | |
43 | "sw-exception", | |
44 | "watchdog", | |
45 | "ce0", | |
46 | "ce1", | |
47 | "ce2", | |
48 | "ce3", | |
49 | "ce4", | |
50 | "ce5", | |
51 | "ce6", | |
52 | "ce7", | |
53 | "ce8", | |
54 | "ce9", | |
55 | "ce10", | |
56 | "ce11", | |
57 | "host2wbm-desc-feed", | |
58 | "host2reo-re-injection", | |
59 | "host2reo-command", | |
60 | "host2rxdma-monitor-ring3", | |
61 | "host2rxdma-monitor-ring2", | |
62 | "host2rxdma-monitor-ring1", | |
63 | "reo2ost-exception", | |
64 | "wbm2host-rx-release", | |
65 | "reo2host-status", | |
66 | "reo2host-destination-ring4", | |
67 | "reo2host-destination-ring3", | |
68 | "reo2host-destination-ring2", | |
69 | "reo2host-destination-ring1", | |
70 | "rxdma2host-monitor-destination-mac3", | |
71 | "rxdma2host-monitor-destination-mac2", | |
72 | "rxdma2host-monitor-destination-mac1", | |
73 | "ppdu-end-interrupts-mac3", | |
74 | "ppdu-end-interrupts-mac2", | |
75 | "ppdu-end-interrupts-mac1", | |
76 | "rxdma2host-monitor-status-ring-mac3", | |
77 | "rxdma2host-monitor-status-ring-mac2", | |
78 | "rxdma2host-monitor-status-ring-mac1", | |
79 | "host2rxdma-host-buf-ring-mac3", | |
80 | "host2rxdma-host-buf-ring-mac2", | |
81 | "host2rxdma-host-buf-ring-mac1", | |
82 | "rxdma2host-destination-ring-mac3", | |
83 | "rxdma2host-destination-ring-mac2", | |
84 | "rxdma2host-destination-ring-mac1", | |
85 | "host2tcl-input-ring4", | |
86 | "host2tcl-input-ring3", | |
87 | "host2tcl-input-ring2", | |
88 | "host2tcl-input-ring1", | |
89 | "wbm2host-tx-completions-ring3", | |
90 | "wbm2host-tx-completions-ring2", | |
91 | "wbm2host-tx-completions-ring1", | |
92 | "tcl2host-status-ring", | |
93 | }; | |
94 | ||
d5c65159 KV |
95 | /* enum ext_irq_num - irq numbers that can be used by external modules |
96 | * like datapath | |
97 | */ | |
98 | enum ext_irq_num { | |
99 | host2wbm_desc_feed = 16, | |
100 | host2reo_re_injection, | |
101 | host2reo_command, | |
102 | host2rxdma_monitor_ring3, | |
103 | host2rxdma_monitor_ring2, | |
104 | host2rxdma_monitor_ring1, | |
105 | reo2host_exception, | |
106 | wbm2host_rx_release, | |
107 | reo2host_status, | |
108 | reo2host_destination_ring4, | |
109 | reo2host_destination_ring3, | |
110 | reo2host_destination_ring2, | |
111 | reo2host_destination_ring1, | |
112 | rxdma2host_monitor_destination_mac3, | |
113 | rxdma2host_monitor_destination_mac2, | |
114 | rxdma2host_monitor_destination_mac1, | |
115 | ppdu_end_interrupts_mac3, | |
116 | ppdu_end_interrupts_mac2, | |
117 | ppdu_end_interrupts_mac1, | |
118 | rxdma2host_monitor_status_ring_mac3, | |
119 | rxdma2host_monitor_status_ring_mac2, | |
120 | rxdma2host_monitor_status_ring_mac1, | |
121 | host2rxdma_host_buf_ring_mac3, | |
122 | host2rxdma_host_buf_ring_mac2, | |
123 | host2rxdma_host_buf_ring_mac1, | |
124 | rxdma2host_destination_ring_mac3, | |
125 | rxdma2host_destination_ring_mac2, | |
126 | rxdma2host_destination_ring_mac1, | |
127 | host2tcl_input_ring4, | |
128 | host2tcl_input_ring3, | |
129 | host2tcl_input_ring2, | |
130 | host2tcl_input_ring1, | |
131 | wbm2host_tx_completions_ring3, | |
132 | wbm2host_tx_completions_ring2, | |
133 | wbm2host_tx_completions_ring1, | |
134 | tcl2host_status_ring, | |
135 | }; | |
136 | ||
00402f49 MP |
137 | static int |
138 | ath11k_ahb_get_msi_irq_wcn6750(struct ath11k_base *ab, unsigned int vector) | |
139 | { | |
140 | return ab->pci.msi.irqs[vector]; | |
141 | } | |
142 | ||
867f4eee MP |
143 | static inline u32 |
144 | ath11k_ahb_get_window_start_wcn6750(struct ath11k_base *ab, u32 offset) | |
145 | { | |
146 | u32 window_start = 0; | |
147 | ||
148 | /* If offset lies within DP register range, use 1st window */ | |
149 | if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK) | |
150 | window_start = ATH11K_PCI_WINDOW_START; | |
151 | /* If offset lies within CE register range, use 2nd window */ | |
152 | else if ((offset ^ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab)) < | |
153 | ATH11K_PCI_WINDOW_RANGE_MASK) | |
154 | window_start = 2 * ATH11K_PCI_WINDOW_START; | |
155 | ||
156 | return window_start; | |
157 | } | |
158 | ||
159 | static void | |
160 | ath11k_ahb_window_write32_wcn6750(struct ath11k_base *ab, u32 offset, u32 value) | |
161 | { | |
162 | u32 window_start; | |
163 | ||
164 | /* WCN6750 uses static window based register access*/ | |
165 | window_start = ath11k_ahb_get_window_start_wcn6750(ab, offset); | |
166 | ||
167 | iowrite32(value, ab->mem + window_start + | |
168 | (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); | |
169 | } | |
170 | ||
171 | static u32 ath11k_ahb_window_read32_wcn6750(struct ath11k_base *ab, u32 offset) | |
172 | { | |
173 | u32 window_start; | |
174 | u32 val; | |
175 | ||
176 | /* WCN6750 uses static window based register access */ | |
177 | window_start = ath11k_ahb_get_window_start_wcn6750(ab, offset); | |
178 | ||
179 | val = ioread32(ab->mem + window_start + | |
180 | (offset & ATH11K_PCI_WINDOW_RANGE_MASK)); | |
181 | return val; | |
182 | } | |
183 | ||
00402f49 | 184 | static const struct ath11k_pci_ops ath11k_ahb_pci_ops_wcn6750 = { |
867f4eee MP |
185 | .wakeup = NULL, |
186 | .release = NULL, | |
00402f49 | 187 | .get_msi_irq = ath11k_ahb_get_msi_irq_wcn6750, |
867f4eee MP |
188 | .window_write32 = ath11k_ahb_window_write32_wcn6750, |
189 | .window_read32 = ath11k_ahb_window_read32_wcn6750, | |
00402f49 MP |
190 | }; |
191 | ||
31858805 GS |
192 | static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) |
193 | { | |
194 | return ioread32(ab->mem + offset); | |
195 | } | |
196 | ||
197 | static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) | |
198 | { | |
199 | iowrite32(value, ab->mem + offset); | |
200 | } | |
201 | ||
d5c65159 KV |
202 | static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) |
203 | { | |
204 | int i; | |
205 | ||
d9d4b5f3 | 206 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
d5c65159 KV |
207 | struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; |
208 | ||
e3396b8b | 209 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
210 | continue; |
211 | ||
212 | tasklet_kill(&ce_pipe->intr_tq); | |
213 | } | |
214 | } | |
215 | ||
216 | static void ath11k_ahb_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) | |
217 | { | |
218 | int i; | |
219 | ||
220 | for (i = 0; i < irq_grp->num_irq; i++) | |
221 | disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
222 | } | |
223 | ||
224 | static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) | |
225 | { | |
d5c65159 KV |
226 | int i; |
227 | ||
228 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
229 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
230 | ||
231 | ath11k_ahb_ext_grp_disable(irq_grp); | |
232 | ||
d943fdad BG |
233 | if (irq_grp->napi_enabled) { |
234 | napi_synchronize(&irq_grp->napi); | |
235 | napi_disable(&irq_grp->napi); | |
236 | irq_grp->napi_enabled = false; | |
237 | } | |
d5c65159 KV |
238 | } |
239 | } | |
240 | ||
241 | static void ath11k_ahb_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) | |
242 | { | |
243 | int i; | |
244 | ||
245 | for (i = 0; i < irq_grp->num_irq; i++) | |
246 | enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
247 | } | |
248 | ||
249 | static void ath11k_ahb_setbit32(struct ath11k_base *ab, u8 bit, u32 offset) | |
250 | { | |
251 | u32 val; | |
252 | ||
253 | val = ath11k_ahb_read32(ab, offset); | |
254 | ath11k_ahb_write32(ab, offset, val | BIT(bit)); | |
255 | } | |
256 | ||
257 | static void ath11k_ahb_clearbit32(struct ath11k_base *ab, u8 bit, u32 offset) | |
258 | { | |
259 | u32 val; | |
260 | ||
261 | val = ath11k_ahb_read32(ab, offset); | |
262 | ath11k_ahb_write32(ab, offset, val & ~BIT(bit)); | |
263 | } | |
264 | ||
265 | static void ath11k_ahb_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) | |
266 | { | |
b689f091 | 267 | const struct ce_attr *ce_attr; |
d5c65159 | 268 | |
b689f091 AK |
269 | ce_attr = &ab->hw_params.host_ce_config[ce_id]; |
270 | if (ce_attr->src_nentries) | |
d5c65159 KV |
271 | ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_ADDRESS); |
272 | ||
b689f091 | 273 | if (ce_attr->dest_nentries) { |
d5c65159 KV |
274 | ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); |
275 | ath11k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, | |
276 | CE_HOST_IE_3_ADDRESS); | |
277 | } | |
278 | } | |
279 | ||
280 | static void ath11k_ahb_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) | |
281 | { | |
b689f091 | 282 | const struct ce_attr *ce_attr; |
d5c65159 | 283 | |
b689f091 AK |
284 | ce_attr = &ab->hw_params.host_ce_config[ce_id]; |
285 | if (ce_attr->src_nentries) | |
d5c65159 KV |
286 | ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_ADDRESS); |
287 | ||
b689f091 | 288 | if (ce_attr->dest_nentries) { |
d5c65159 KV |
289 | ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); |
290 | ath11k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, | |
291 | CE_HOST_IE_3_ADDRESS); | |
292 | } | |
293 | } | |
294 | ||
295 | static void ath11k_ahb_sync_ce_irqs(struct ath11k_base *ab) | |
296 | { | |
297 | int i; | |
298 | int irq_idx; | |
299 | ||
d9d4b5f3 | 300 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
e3396b8b | 301 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
302 | continue; |
303 | ||
304 | irq_idx = ATH11K_IRQ_CE0_OFFSET + i; | |
305 | synchronize_irq(ab->irq_num[irq_idx]); | |
306 | } | |
307 | } | |
308 | ||
309 | static void ath11k_ahb_sync_ext_irqs(struct ath11k_base *ab) | |
310 | { | |
311 | int i, j; | |
312 | int irq_idx; | |
313 | ||
314 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
315 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
316 | ||
317 | for (j = 0; j < irq_grp->num_irq; j++) { | |
318 | irq_idx = irq_grp->irqs[j]; | |
319 | synchronize_irq(ab->irq_num[irq_idx]); | |
320 | } | |
321 | } | |
322 | } | |
323 | ||
324 | static void ath11k_ahb_ce_irqs_enable(struct ath11k_base *ab) | |
325 | { | |
326 | int i; | |
327 | ||
d9d4b5f3 | 328 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
e3396b8b | 329 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
330 | continue; |
331 | ath11k_ahb_ce_irq_enable(ab, i); | |
332 | } | |
333 | } | |
334 | ||
335 | static void ath11k_ahb_ce_irqs_disable(struct ath11k_base *ab) | |
336 | { | |
337 | int i; | |
338 | ||
d9d4b5f3 | 339 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
e3396b8b | 340 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
341 | continue; |
342 | ath11k_ahb_ce_irq_disable(ab, i); | |
343 | } | |
344 | } | |
345 | ||
31858805 | 346 | static int ath11k_ahb_start(struct ath11k_base *ab) |
d5c65159 KV |
347 | { |
348 | ath11k_ahb_ce_irqs_enable(ab); | |
349 | ath11k_ce_rx_post_buf(ab); | |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
31858805 | 354 | static void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) |
d5c65159 KV |
355 | { |
356 | int i; | |
357 | ||
358 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
359 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
360 | ||
d943fdad BG |
361 | if (!irq_grp->napi_enabled) { |
362 | napi_enable(&irq_grp->napi); | |
363 | irq_grp->napi_enabled = true; | |
364 | } | |
d5c65159 KV |
365 | ath11k_ahb_ext_grp_enable(irq_grp); |
366 | } | |
367 | } | |
368 | ||
31858805 | 369 | static void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) |
d5c65159 KV |
370 | { |
371 | __ath11k_ahb_ext_irq_disable(ab); | |
372 | ath11k_ahb_sync_ext_irqs(ab); | |
373 | } | |
374 | ||
31858805 | 375 | static void ath11k_ahb_stop(struct ath11k_base *ab) |
d5c65159 KV |
376 | { |
377 | if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) | |
378 | ath11k_ahb_ce_irqs_disable(ab); | |
379 | ath11k_ahb_sync_ce_irqs(ab); | |
380 | ath11k_ahb_kill_tasklets(ab); | |
381 | del_timer_sync(&ab->rx_replenish_retry); | |
382 | ath11k_ce_cleanup_pipes(ab); | |
383 | } | |
384 | ||
31858805 | 385 | static int ath11k_ahb_power_up(struct ath11k_base *ab) |
d5c65159 | 386 | { |
ba929d6f | 387 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); |
d5c65159 KV |
388 | int ret; |
389 | ||
ba929d6f | 390 | ret = rproc_boot(ab_ahb->tgt_rproc); |
d5c65159 KV |
391 | if (ret) |
392 | ath11k_err(ab, "failed to boot the remote processor Q6\n"); | |
393 | ||
394 | return ret; | |
395 | } | |
396 | ||
31858805 | 397 | static void ath11k_ahb_power_down(struct ath11k_base *ab) |
d5c65159 | 398 | { |
ba929d6f GS |
399 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); |
400 | ||
401 | rproc_shutdown(ab_ahb->tgt_rproc); | |
d5c65159 KV |
402 | } |
403 | ||
02f9d3c1 GS |
404 | static int ath11k_ahb_fwreset_from_cold_boot(struct ath11k_base *ab) |
405 | { | |
406 | int timeout; | |
407 | ||
408 | if (ath11k_cold_boot_cal == 0 || ab->qmi.cal_done || | |
409 | ab->hw_params.cold_boot_calib == 0) | |
410 | return 0; | |
411 | ||
412 | ath11k_dbg(ab, ATH11K_DBG_AHB, "wait for cold boot done\n"); | |
413 | timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, | |
414 | (ab->qmi.cal_done == 1), | |
415 | ATH11K_COLD_BOOT_FW_RESET_DELAY); | |
416 | if (timeout <= 0) { | |
417 | ath11k_cold_boot_cal = 0; | |
418 | ath11k_warn(ab, "Coldboot Calibration failed timed out\n"); | |
419 | } | |
420 | ||
421 | /* reset the firmware */ | |
422 | ath11k_ahb_power_down(ab); | |
423 | ath11k_ahb_power_up(ab); | |
424 | ||
425 | ath11k_dbg(ab, ATH11K_DBG_AHB, "exited from cold boot mode\n"); | |
426 | return 0; | |
427 | } | |
428 | ||
d5c65159 KV |
429 | static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) |
430 | { | |
431 | struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; | |
432 | ||
967c1d11 AK |
433 | cfg->tgt_ce_len = ab->hw_params.target_ce_count; |
434 | cfg->tgt_ce = ab->hw_params.target_ce_config; | |
435 | cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len; | |
436 | cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map; | |
16001e4b | 437 | ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; |
d5c65159 KV |
438 | } |
439 | ||
440 | static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) | |
441 | { | |
442 | int i, j; | |
443 | ||
444 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
445 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
446 | ||
447 | for (j = 0; j < irq_grp->num_irq; j++) | |
448 | free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); | |
22b59cb9 VN |
449 | |
450 | netif_napi_del(&irq_grp->napi); | |
d5c65159 KV |
451 | } |
452 | } | |
453 | ||
454 | static void ath11k_ahb_free_irq(struct ath11k_base *ab) | |
455 | { | |
456 | int irq_idx; | |
457 | int i; | |
458 | ||
00402f49 MP |
459 | if (ab->hw_params.hybrid_bus_type) |
460 | return ath11k_pcic_free_irq(ab); | |
461 | ||
d9d4b5f3 | 462 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
e3396b8b | 463 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
464 | continue; |
465 | irq_idx = ATH11K_IRQ_CE0_OFFSET + i; | |
466 | free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); | |
467 | } | |
468 | ||
469 | ath11k_ahb_free_ext_irq(ab); | |
470 | } | |
471 | ||
c08279a9 | 472 | static void ath11k_ahb_ce_tasklet(struct tasklet_struct *t) |
d5c65159 | 473 | { |
c08279a9 | 474 | struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); |
d5c65159 KV |
475 | |
476 | ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); | |
477 | ||
478 | ath11k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); | |
479 | } | |
480 | ||
481 | static irqreturn_t ath11k_ahb_ce_interrupt_handler(int irq, void *arg) | |
482 | { | |
483 | struct ath11k_ce_pipe *ce_pipe = arg; | |
484 | ||
5118935b MP |
485 | /* last interrupt received for this CE */ |
486 | ce_pipe->timestamp = jiffies; | |
487 | ||
d5c65159 KV |
488 | ath11k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); |
489 | ||
490 | tasklet_schedule(&ce_pipe->intr_tq); | |
491 | ||
492 | return IRQ_HANDLED; | |
493 | } | |
494 | ||
495 | static int ath11k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) | |
496 | { | |
497 | struct ath11k_ext_irq_grp *irq_grp = container_of(napi, | |
498 | struct ath11k_ext_irq_grp, | |
499 | napi); | |
500 | struct ath11k_base *ab = irq_grp->ab; | |
501 | int work_done; | |
502 | ||
503 | work_done = ath11k_dp_service_srng(ab, irq_grp, budget); | |
504 | if (work_done < budget) { | |
505 | napi_complete_done(napi, work_done); | |
506 | ath11k_ahb_ext_grp_enable(irq_grp); | |
507 | } | |
508 | ||
509 | if (work_done > budget) | |
510 | work_done = budget; | |
511 | ||
512 | return work_done; | |
513 | } | |
514 | ||
515 | static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) | |
516 | { | |
517 | struct ath11k_ext_irq_grp *irq_grp = arg; | |
518 | ||
5118935b MP |
519 | /* last interrupt received for this group */ |
520 | irq_grp->timestamp = jiffies; | |
521 | ||
d5c65159 KV |
522 | ath11k_ahb_ext_grp_disable(irq_grp); |
523 | ||
524 | napi_schedule(&irq_grp->napi); | |
525 | ||
526 | return IRQ_HANDLED; | |
527 | } | |
528 | ||
a76ed591 | 529 | static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) |
d5c65159 | 530 | { |
d547ca4c | 531 | struct ath11k_hw_params *hw = &ab->hw_params; |
d5c65159 KV |
532 | int i, j; |
533 | int irq; | |
534 | int ret; | |
535 | ||
536 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
537 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
538 | u32 num_irq = 0; | |
539 | ||
540 | irq_grp->ab = ab; | |
541 | irq_grp->grp_id = i; | |
542 | init_dummy_netdev(&irq_grp->napi_ndev); | |
543 | netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, | |
b48b89f9 | 544 | ath11k_ahb_ext_grp_napi_poll); |
d5c65159 KV |
545 | |
546 | for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { | |
34d5a3a8 | 547 | if (ab->hw_params.ring_mask->tx[i] & BIT(j)) { |
d5c65159 KV |
548 | irq_grp->irqs[num_irq++] = |
549 | wbm2host_tx_completions_ring1 - j; | |
550 | } | |
551 | ||
34d5a3a8 | 552 | if (ab->hw_params.ring_mask->rx[i] & BIT(j)) { |
d5c65159 KV |
553 | irq_grp->irqs[num_irq++] = |
554 | reo2host_destination_ring1 - j; | |
555 | } | |
556 | ||
34d5a3a8 | 557 | if (ab->hw_params.ring_mask->rx_err[i] & BIT(j)) |
d5c65159 KV |
558 | irq_grp->irqs[num_irq++] = reo2host_exception; |
559 | ||
34d5a3a8 | 560 | if (ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j)) |
d5c65159 KV |
561 | irq_grp->irqs[num_irq++] = wbm2host_rx_release; |
562 | ||
34d5a3a8 | 563 | if (ab->hw_params.ring_mask->reo_status[i] & BIT(j)) |
d5c65159 KV |
564 | irq_grp->irqs[num_irq++] = reo2host_status; |
565 | ||
d547ca4c | 566 | if (j < ab->hw_params.max_radios) { |
34d5a3a8 | 567 | if (ab->hw_params.ring_mask->rxdma2host[i] & BIT(j)) { |
d5c65159 | 568 | irq_grp->irqs[num_irq++] = |
d547ca4c AK |
569 | rxdma2host_destination_ring_mac1 - |
570 | ath11k_hw_get_mac_from_pdev_id(hw, j); | |
d5c65159 KV |
571 | } |
572 | ||
34d5a3a8 | 573 | if (ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) { |
d5c65159 | 574 | irq_grp->irqs[num_irq++] = |
d547ca4c AK |
575 | host2rxdma_host_buf_ring_mac1 - |
576 | ath11k_hw_get_mac_from_pdev_id(hw, j); | |
d5c65159 KV |
577 | } |
578 | ||
34d5a3a8 | 579 | if (ab->hw_params.ring_mask->rx_mon_status[i] & BIT(j)) { |
d5c65159 KV |
580 | irq_grp->irqs[num_irq++] = |
581 | ppdu_end_interrupts_mac1 - | |
d547ca4c | 582 | ath11k_hw_get_mac_from_pdev_id(hw, j); |
d5c65159 KV |
583 | irq_grp->irqs[num_irq++] = |
584 | rxdma2host_monitor_status_ring_mac1 - | |
d547ca4c | 585 | ath11k_hw_get_mac_from_pdev_id(hw, j); |
d5c65159 KV |
586 | } |
587 | } | |
588 | } | |
589 | irq_grp->num_irq = num_irq; | |
590 | ||
591 | for (j = 0; j < irq_grp->num_irq; j++) { | |
592 | int irq_idx = irq_grp->irqs[j]; | |
593 | ||
594 | irq = platform_get_irq_byname(ab->pdev, | |
595 | irq_name[irq_idx]); | |
596 | ab->irq_num[irq_idx] = irq; | |
05090864 | 597 | irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY); |
d5c65159 KV |
598 | ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler, |
599 | IRQF_TRIGGER_RISING, | |
600 | irq_name[irq_idx], irq_grp); | |
601 | if (ret) { | |
602 | ath11k_err(ab, "failed request_irq for %d\n", | |
603 | irq); | |
604 | } | |
605 | } | |
606 | } | |
607 | ||
608 | return 0; | |
609 | } | |
610 | ||
611 | static int ath11k_ahb_config_irq(struct ath11k_base *ab) | |
612 | { | |
613 | int irq, irq_idx, i; | |
614 | int ret; | |
615 | ||
00402f49 MP |
616 | if (ab->hw_params.hybrid_bus_type) |
617 | return ath11k_pcic_config_irq(ab); | |
618 | ||
d5c65159 | 619 | /* Configure CE irqs */ |
d9d4b5f3 | 620 | for (i = 0; i < ab->hw_params.ce_count; i++) { |
d5c65159 KV |
621 | struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; |
622 | ||
e3396b8b | 623 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) |
d5c65159 KV |
624 | continue; |
625 | ||
626 | irq_idx = ATH11K_IRQ_CE0_OFFSET + i; | |
627 | ||
c08279a9 | 628 | tasklet_setup(&ce_pipe->intr_tq, ath11k_ahb_ce_tasklet); |
d5c65159 KV |
629 | irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); |
630 | ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler, | |
631 | IRQF_TRIGGER_RISING, irq_name[irq_idx], | |
632 | ce_pipe); | |
633 | if (ret) | |
634 | return ret; | |
635 | ||
636 | ab->irq_num[irq_idx] = irq; | |
637 | } | |
638 | ||
639 | /* Configure external interrupts */ | |
a76ed591 | 640 | ret = ath11k_ahb_config_ext_irq(ab); |
d5c65159 KV |
641 | |
642 | return ret; | |
643 | } | |
644 | ||
31858805 GS |
645 | static int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, |
646 | u8 *ul_pipe, u8 *dl_pipe) | |
d5c65159 KV |
647 | { |
648 | const struct service_to_pipe *entry; | |
649 | bool ul_set = false, dl_set = false; | |
650 | int i; | |
651 | ||
967c1d11 AK |
652 | for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) { |
653 | entry = &ab->hw_params.svc_to_ce_map[i]; | |
d5c65159 KV |
654 | |
655 | if (__le32_to_cpu(entry->service_id) != service_id) | |
656 | continue; | |
657 | ||
658 | switch (__le32_to_cpu(entry->pipedir)) { | |
659 | case PIPEDIR_NONE: | |
660 | break; | |
661 | case PIPEDIR_IN: | |
662 | WARN_ON(dl_set); | |
663 | *dl_pipe = __le32_to_cpu(entry->pipenum); | |
664 | dl_set = true; | |
665 | break; | |
666 | case PIPEDIR_OUT: | |
667 | WARN_ON(ul_set); | |
668 | *ul_pipe = __le32_to_cpu(entry->pipenum); | |
669 | ul_set = true; | |
670 | break; | |
671 | case PIPEDIR_INOUT: | |
672 | WARN_ON(dl_set); | |
673 | WARN_ON(ul_set); | |
674 | *dl_pipe = __le32_to_cpu(entry->pipenum); | |
675 | *ul_pipe = __le32_to_cpu(entry->pipenum); | |
676 | dl_set = true; | |
677 | ul_set = true; | |
678 | break; | |
679 | } | |
680 | } | |
681 | ||
682 | if (WARN_ON(!ul_set || !dl_set)) | |
683 | return -ENOENT; | |
684 | ||
685 | return 0; | |
686 | } | |
687 | ||
00402f49 | 688 | static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = { |
31858805 GS |
689 | .start = ath11k_ahb_start, |
690 | .stop = ath11k_ahb_stop, | |
691 | .read32 = ath11k_ahb_read32, | |
692 | .write32 = ath11k_ahb_write32, | |
693 | .irq_enable = ath11k_ahb_ext_irq_enable, | |
694 | .irq_disable = ath11k_ahb_ext_irq_disable, | |
695 | .map_service_to_pipe = ath11k_ahb_map_service_to_pipe, | |
696 | .power_down = ath11k_ahb_power_down, | |
697 | .power_up = ath11k_ahb_power_up, | |
698 | }; | |
699 | ||
00402f49 MP |
700 | static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = { |
701 | .start = ath11k_pcic_start, | |
702 | .stop = ath11k_pcic_stop, | |
703 | .read32 = ath11k_pcic_read32, | |
704 | .write32 = ath11k_pcic_write32, | |
705 | .irq_enable = ath11k_pcic_ext_irq_enable, | |
706 | .irq_disable = ath11k_pcic_ext_irq_disable, | |
707 | .get_msi_address = ath11k_pcic_get_msi_address, | |
708 | .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment, | |
709 | .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, | |
710 | .power_down = ath11k_ahb_power_down, | |
711 | .power_up = ath11k_ahb_power_up, | |
712 | }; | |
713 | ||
ba929d6f GS |
714 | static int ath11k_core_get_rproc(struct ath11k_base *ab) |
715 | { | |
716 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); | |
717 | struct device *dev = ab->dev; | |
718 | struct rproc *prproc; | |
719 | phandle rproc_phandle; | |
720 | ||
721 | if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { | |
722 | ath11k_err(ab, "failed to get q6_rproc handle\n"); | |
723 | return -ENOENT; | |
724 | } | |
725 | ||
726 | prproc = rproc_get_by_phandle(rproc_phandle); | |
727 | if (!prproc) { | |
728 | ath11k_err(ab, "failed to get rproc\n"); | |
729 | return -EINVAL; | |
730 | } | |
731 | ab_ahb->tgt_rproc = prproc; | |
732 | ||
733 | return 0; | |
734 | } | |
735 | ||
00402f49 MP |
736 | static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab) |
737 | { | |
738 | struct platform_device *pdev = ab->pdev; | |
739 | phys_addr_t msi_addr_pa; | |
740 | dma_addr_t msi_addr_iova; | |
741 | struct resource *res; | |
742 | int int_prop; | |
743 | int ret; | |
744 | int i; | |
745 | ||
746 | ret = ath11k_pcic_init_msi_config(ab); | |
747 | if (ret) { | |
748 | ath11k_err(ab, "failed to init msi config: %d\n", ret); | |
749 | return ret; | |
750 | } | |
751 | ||
752 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
753 | if (!res) { | |
754 | ath11k_err(ab, "failed to fetch msi_addr\n"); | |
755 | return -ENOENT; | |
756 | } | |
757 | ||
758 | msi_addr_pa = res->start; | |
759 | msi_addr_iova = dma_map_resource(ab->dev, msi_addr_pa, PAGE_SIZE, | |
760 | DMA_FROM_DEVICE, 0); | |
761 | if (dma_mapping_error(ab->dev, msi_addr_iova)) | |
762 | return -ENOMEM; | |
763 | ||
764 | ab->pci.msi.addr_lo = lower_32_bits(msi_addr_iova); | |
765 | ab->pci.msi.addr_hi = upper_32_bits(msi_addr_iova); | |
766 | ||
767 | ret = of_property_read_u32_index(ab->dev->of_node, "interrupts", 1, &int_prop); | |
768 | if (ret) | |
769 | return ret; | |
770 | ||
771 | ab->pci.msi.ep_base_data = int_prop + 32; | |
772 | ||
773 | for (i = 0; i < ab->pci.msi.config->total_vectors; i++) { | |
774 | res = platform_get_resource(pdev, IORESOURCE_IRQ, i); | |
775 | if (!res) | |
776 | return -ENODEV; | |
777 | ||
778 | ab->pci.msi.irqs[i] = res->start; | |
779 | } | |
780 | ||
781 | set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); | |
782 | ||
783 | return 0; | |
784 | } | |
785 | ||
786 | static int ath11k_ahb_setup_resources(struct ath11k_base *ab) | |
787 | { | |
788 | struct platform_device *pdev = ab->pdev; | |
789 | struct resource *mem_res; | |
790 | void __iomem *mem; | |
791 | ||
792 | if (ab->hw_params.hybrid_bus_type) | |
793 | return ath11k_ahb_setup_msi_resources(ab); | |
794 | ||
795 | mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); | |
796 | if (IS_ERR(mem)) { | |
797 | dev_err(&pdev->dev, "ioremap error\n"); | |
798 | return PTR_ERR(mem); | |
799 | } | |
800 | ||
801 | ab->mem = mem; | |
802 | ab->mem_len = resource_size(mem_res); | |
803 | ||
804 | return 0; | |
805 | } | |
806 | ||
f9eec494 MP |
807 | static int ath11k_ahb_setup_msa_resources(struct ath11k_base *ab) |
808 | { | |
809 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); | |
810 | struct device *dev = ab->dev; | |
811 | struct device_node *node; | |
812 | struct resource r; | |
813 | int ret; | |
814 | ||
815 | node = of_parse_phandle(dev->of_node, "memory-region", 0); | |
816 | if (!node) | |
817 | return -ENOENT; | |
818 | ||
819 | ret = of_address_to_resource(node, 0, &r); | |
820 | of_node_put(node); | |
821 | if (ret) { | |
822 | dev_err(dev, "failed to resolve msa fixed region\n"); | |
823 | return ret; | |
824 | } | |
825 | ||
826 | ab_ahb->fw.msa_paddr = r.start; | |
827 | ab_ahb->fw.msa_size = resource_size(&r); | |
828 | ||
829 | node = of_parse_phandle(dev->of_node, "memory-region", 1); | |
830 | if (!node) | |
831 | return -ENOENT; | |
832 | ||
833 | ret = of_address_to_resource(node, 0, &r); | |
834 | of_node_put(node); | |
835 | if (ret) { | |
836 | dev_err(dev, "failed to resolve ce fixed region\n"); | |
837 | return ret; | |
838 | } | |
839 | ||
840 | ab_ahb->fw.ce_paddr = r.start; | |
841 | ab_ahb->fw.ce_size = resource_size(&r); | |
842 | ||
843 | return 0; | |
844 | } | |
845 | ||
846 | static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) | |
847 | { | |
848 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); | |
849 | struct device *host_dev = ab->dev; | |
850 | struct platform_device_info info = {0}; | |
851 | struct iommu_domain *iommu_dom; | |
852 | struct platform_device *pdev; | |
853 | struct device_node *node; | |
854 | int ret; | |
855 | ||
856 | /* Chipsets not requiring MSA need not initialize | |
857 | * MSA resources, return success in such cases. | |
858 | */ | |
859 | if (!ab->hw_params.fixed_fw_mem) | |
860 | return 0; | |
861 | ||
862 | ret = ath11k_ahb_setup_msa_resources(ab); | |
863 | if (ret) { | |
864 | ath11k_err(ab, "failed to setup msa resources\n"); | |
865 | return ret; | |
866 | } | |
867 | ||
868 | node = of_get_child_by_name(host_dev->of_node, "wifi-firmware"); | |
869 | if (!node) { | |
870 | ab_ahb->fw.use_tz = true; | |
871 | return 0; | |
872 | } | |
873 | ||
874 | info.fwnode = &node->fwnode; | |
875 | info.parent = host_dev; | |
876 | info.name = node->name; | |
877 | info.dma_mask = DMA_BIT_MASK(32); | |
878 | ||
879 | pdev = platform_device_register_full(&info); | |
880 | if (IS_ERR(pdev)) { | |
881 | of_node_put(node); | |
882 | return PTR_ERR(pdev); | |
883 | } | |
884 | ||
885 | ret = of_dma_configure(&pdev->dev, node, true); | |
886 | if (ret) { | |
887 | ath11k_err(ab, "dma configure fail: %d\n", ret); | |
888 | goto err_unregister; | |
889 | } | |
890 | ||
891 | ab_ahb->fw.dev = &pdev->dev; | |
892 | ||
893 | iommu_dom = iommu_domain_alloc(&platform_bus_type); | |
894 | if (!iommu_dom) { | |
895 | ath11k_err(ab, "failed to allocate iommu domain\n"); | |
896 | ret = -ENOMEM; | |
897 | goto err_unregister; | |
898 | } | |
899 | ||
900 | ret = iommu_attach_device(iommu_dom, ab_ahb->fw.dev); | |
901 | if (ret) { | |
902 | ath11k_err(ab, "could not attach device: %d\n", ret); | |
903 | goto err_iommu_free; | |
904 | } | |
905 | ||
906 | ret = iommu_map(iommu_dom, ab_ahb->fw.msa_paddr, | |
907 | ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size, | |
908 | IOMMU_READ | IOMMU_WRITE); | |
909 | if (ret) { | |
910 | ath11k_err(ab, "failed to map firmware region: %d\n", ret); | |
911 | goto err_iommu_detach; | |
912 | } | |
913 | ||
914 | ret = iommu_map(iommu_dom, ab_ahb->fw.ce_paddr, | |
915 | ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size, | |
916 | IOMMU_READ | IOMMU_WRITE); | |
917 | if (ret) { | |
918 | ath11k_err(ab, "failed to map firmware CE region: %d\n", ret); | |
919 | goto err_iommu_unmap; | |
920 | } | |
921 | ||
922 | ab_ahb->fw.use_tz = false; | |
923 | ab_ahb->fw.iommu_domain = iommu_dom; | |
924 | of_node_put(node); | |
925 | ||
926 | return 0; | |
927 | ||
928 | err_iommu_unmap: | |
929 | iommu_unmap(iommu_dom, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size); | |
930 | ||
931 | err_iommu_detach: | |
932 | iommu_detach_device(iommu_dom, ab_ahb->fw.dev); | |
933 | ||
934 | err_iommu_free: | |
935 | iommu_domain_free(iommu_dom); | |
936 | ||
937 | err_unregister: | |
938 | platform_device_unregister(pdev); | |
939 | of_node_put(node); | |
940 | ||
941 | return ret; | |
942 | } | |
943 | ||
944 | static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab) | |
945 | { | |
946 | struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); | |
947 | struct iommu_domain *iommu; | |
948 | size_t unmapped_size; | |
949 | ||
950 | if (ab_ahb->fw.use_tz) | |
951 | return 0; | |
952 | ||
953 | iommu = ab_ahb->fw.iommu_domain; | |
954 | ||
955 | unmapped_size = iommu_unmap(iommu, ab_ahb->fw.msa_paddr, ab_ahb->fw.msa_size); | |
956 | if (unmapped_size != ab_ahb->fw.msa_size) | |
957 | ath11k_err(ab, "failed to unmap firmware: %zu\n", | |
958 | unmapped_size); | |
959 | ||
960 | unmapped_size = iommu_unmap(iommu, ab_ahb->fw.ce_paddr, ab_ahb->fw.ce_size); | |
961 | if (unmapped_size != ab_ahb->fw.ce_size) | |
962 | ath11k_err(ab, "failed to unmap firmware CE memory: %zu\n", | |
963 | unmapped_size); | |
964 | ||
965 | iommu_detach_device(iommu, ab_ahb->fw.dev); | |
966 | iommu_domain_free(iommu); | |
967 | ||
968 | platform_device_unregister(to_platform_device(ab_ahb->fw.dev)); | |
969 | ||
970 | return 0; | |
971 | } | |
972 | ||
d5c65159 KV |
973 | static int ath11k_ahb_probe(struct platform_device *pdev) |
974 | { | |
975 | struct ath11k_base *ab; | |
976 | const struct of_device_id *of_id; | |
00402f49 MP |
977 | const struct ath11k_hif_ops *hif_ops; |
978 | const struct ath11k_pci_ops *pci_ops; | |
979 | enum ath11k_hw_rev hw_rev; | |
d5c65159 KV |
980 | int ret; |
981 | ||
982 | of_id = of_match_device(ath11k_ahb_of_match, &pdev->dev); | |
983 | if (!of_id) { | |
984 | dev_err(&pdev->dev, "failed to find matching device tree id\n"); | |
985 | return -EINVAL; | |
986 | } | |
987 | ||
00402f49 MP |
988 | hw_rev = (enum ath11k_hw_rev)of_id->data; |
989 | ||
990 | switch (hw_rev) { | |
991 | case ATH11K_HW_IPQ8074: | |
992 | case ATH11K_HW_IPQ6018_HW10: | |
993 | hif_ops = &ath11k_ahb_hif_ops_ipq8074; | |
994 | pci_ops = NULL; | |
995 | break; | |
996 | case ATH11K_HW_WCN6750_HW10: | |
997 | hif_ops = &ath11k_ahb_hif_ops_wcn6750; | |
998 | pci_ops = &ath11k_ahb_pci_ops_wcn6750; | |
999 | break; | |
1000 | default: | |
1001 | dev_err(&pdev->dev, "unsupported device type %d\n", hw_rev); | |
1002 | return -EOPNOTSUPP; | |
d5c65159 KV |
1003 | } |
1004 | ||
1005 | ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); | |
1006 | if (ret) { | |
1007 | dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); | |
1008 | return ret; | |
1009 | } | |
1010 | ||
ba929d6f | 1011 | ab = ath11k_core_alloc(&pdev->dev, sizeof(struct ath11k_ahb), |
92c1858e | 1012 | ATH11K_BUS_AHB); |
d5c65159 KV |
1013 | if (!ab) { |
1014 | dev_err(&pdev->dev, "failed to allocate ath11k base\n"); | |
1015 | return -ENOMEM; | |
1016 | } | |
1017 | ||
00402f49 | 1018 | ab->hif.ops = hif_ops; |
d5c65159 | 1019 | ab->pdev = pdev; |
00402f49 | 1020 | ab->hw_rev = hw_rev; |
d5c65159 KV |
1021 | platform_set_drvdata(pdev, ab); |
1022 | ||
867f4eee MP |
1023 | ret = ath11k_pcic_register_pci_ops(ab, pci_ops); |
1024 | if (ret) { | |
1025 | ath11k_err(ab, "failed to register PCI ops: %d\n", ret); | |
1026 | goto err_core_free; | |
1027 | } | |
1028 | ||
bebcfd25 | 1029 | ret = ath11k_core_pre_init(ab); |
00402f49 MP |
1030 | if (ret) |
1031 | goto err_core_free; | |
1032 | ||
bebcfd25 | 1033 | ret = ath11k_ahb_setup_resources(ab); |
b8246f88 KV |
1034 | if (ret) |
1035 | goto err_core_free; | |
1036 | ||
f9eec494 | 1037 | ret = ath11k_ahb_fw_resources_init(ab); |
d5c65159 KV |
1038 | if (ret) |
1039 | goto err_core_free; | |
1040 | ||
f9eec494 MP |
1041 | ret = ath11k_hal_srng_init(ab); |
1042 | if (ret) | |
1043 | goto err_fw_deinit; | |
1044 | ||
d5c65159 KV |
1045 | ret = ath11k_ce_alloc_pipes(ab); |
1046 | if (ret) { | |
1047 | ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); | |
1048 | goto err_hal_srng_deinit; | |
1049 | } | |
1050 | ||
1051 | ath11k_ahb_init_qmi_ce_config(ab); | |
1052 | ||
ba929d6f GS |
1053 | ret = ath11k_core_get_rproc(ab); |
1054 | if (ret) { | |
1055 | ath11k_err(ab, "failed to get rproc: %d\n", ret); | |
1056 | goto err_ce_free; | |
1057 | } | |
1058 | ||
166e22b3 | 1059 | ret = ath11k_core_init(ab); |
d5c65159 | 1060 | if (ret) { |
166e22b3 | 1061 | ath11k_err(ab, "failed to init core: %d\n", ret); |
d5c65159 KV |
1062 | goto err_ce_free; |
1063 | } | |
1064 | ||
166e22b3 | 1065 | ret = ath11k_ahb_config_irq(ab); |
d5c65159 | 1066 | if (ret) { |
166e22b3 | 1067 | ath11k_err(ab, "failed to configure irq: %d\n", ret); |
d5c65159 KV |
1068 | goto err_ce_free; |
1069 | } | |
1070 | ||
02f9d3c1 GS |
1071 | ath11k_ahb_fwreset_from_cold_boot(ab); |
1072 | ||
d5c65159 KV |
1073 | return 0; |
1074 | ||
1075 | err_ce_free: | |
1076 | ath11k_ce_free_pipes(ab); | |
1077 | ||
1078 | err_hal_srng_deinit: | |
1079 | ath11k_hal_srng_deinit(ab); | |
1080 | ||
f9eec494 MP |
1081 | err_fw_deinit: |
1082 | ath11k_ahb_fw_resource_deinit(ab); | |
1083 | ||
d5c65159 KV |
1084 | err_core_free: |
1085 | ath11k_core_free(ab); | |
1086 | platform_set_drvdata(pdev, NULL); | |
1087 | ||
1088 | return ret; | |
1089 | } | |
1090 | ||
1091 | static int ath11k_ahb_remove(struct platform_device *pdev) | |
1092 | { | |
1093 | struct ath11k_base *ab = platform_get_drvdata(pdev); | |
80b892fc | 1094 | unsigned long left; |
d5c65159 | 1095 | |
61a57e51 AK |
1096 | if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { |
1097 | ath11k_ahb_power_down(ab); | |
1098 | ath11k_debugfs_soc_destroy(ab); | |
1099 | ath11k_qmi_deinit_service(ab); | |
1100 | goto qmi_fail; | |
1101 | } | |
1102 | ||
d5c65159 KV |
1103 | reinit_completion(&ab->driver_recovery); |
1104 | ||
80b892fc BY |
1105 | if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags)) { |
1106 | left = wait_for_completion_timeout(&ab->driver_recovery, | |
1107 | ATH11K_AHB_RECOVERY_TIMEOUT); | |
1108 | if (!left) | |
1109 | ath11k_warn(ab, "failed to receive recovery response completion\n"); | |
1110 | } | |
d5c65159 KV |
1111 | |
1112 | set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); | |
1113 | cancel_work_sync(&ab->restart_work); | |
1114 | ||
1115 | ath11k_core_deinit(ab); | |
61a57e51 | 1116 | qmi_fail: |
d5c65159 | 1117 | ath11k_ahb_free_irq(ab); |
d5c65159 | 1118 | ath11k_hal_srng_deinit(ab); |
f9eec494 | 1119 | ath11k_ahb_fw_resource_deinit(ab); |
d5c65159 KV |
1120 | ath11k_ce_free_pipes(ab); |
1121 | ath11k_core_free(ab); | |
1122 | platform_set_drvdata(pdev, NULL); | |
1123 | ||
1124 | return 0; | |
1125 | } | |
1126 | ||
1127 | static struct platform_driver ath11k_ahb_driver = { | |
1128 | .driver = { | |
1129 | .name = "ath11k", | |
1130 | .of_match_table = ath11k_ahb_of_match, | |
1131 | }, | |
1132 | .probe = ath11k_ahb_probe, | |
1133 | .remove = ath11k_ahb_remove, | |
1134 | }; | |
1135 | ||
31858805 | 1136 | static int ath11k_ahb_init(void) |
d5c65159 KV |
1137 | { |
1138 | return platform_driver_register(&ath11k_ahb_driver); | |
1139 | } | |
31858805 | 1140 | module_init(ath11k_ahb_init); |
d5c65159 | 1141 | |
31858805 | 1142 | static void ath11k_ahb_exit(void) |
d5c65159 KV |
1143 | { |
1144 | platform_driver_unregister(&ath11k_ahb_driver); | |
1145 | } | |
31858805 GS |
1146 | module_exit(ath11k_ahb_exit); |
1147 | ||
6e0355af | 1148 | MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN AHB devices"); |
31858805 | 1149 | MODULE_LICENSE("Dual BSD/GPL"); |