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