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