xref: /openbmc/linux/drivers/net/wireless/ath/ath11k/pci.c (revision e838c14a9ee1dfe660527128d8f2e3191c8b1aad)
16e0355afSGovind Singh // SPDX-License-Identifier: BSD-3-Clause-Clear
26e0355afSGovind Singh /*
36e0355afSGovind Singh  * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
46e0355afSGovind Singh  */
56e0355afSGovind Singh 
66e0355afSGovind Singh #include <linux/module.h>
75697a564SGovind Singh #include <linux/msi.h>
86e0355afSGovind Singh #include <linux/pci.h>
96e0355afSGovind Singh 
105762613eSGovind Singh #include "pci.h"
116e0355afSGovind Singh #include "core.h"
121399fb87SGovind Singh #include "hif.h"
131399fb87SGovind Singh #include "mhi.h"
146e0355afSGovind Singh #include "debug.h"
156e0355afSGovind Singh 
165762613eSGovind Singh #define ATH11K_PCI_BAR_NUM		0
175762613eSGovind Singh #define ATH11K_PCI_DMA_MASK		32
185762613eSGovind Singh 
197f4beda2SGovind Singh #define ATH11K_PCI_IRQ_CE0_OFFSET		3
207f4beda2SGovind Singh 
21654e959aSGovind Singh #define WINDOW_ENABLE_BIT		0x40000000
22654e959aSGovind Singh #define WINDOW_REG_ADDRESS		0x310c
23654e959aSGovind Singh #define WINDOW_VALUE_MASK		GENMASK(24, 19)
24654e959aSGovind Singh #define WINDOW_START			0x80000
25654e959aSGovind Singh #define WINDOW_RANGE_MASK		GENMASK(18, 0)
26654e959aSGovind Singh 
2718ac1665SKalle Valo #define TCSR_SOC_HW_VERSION		0x0224
2818ac1665SKalle Valo #define TCSR_SOC_HW_VERSION_MAJOR_MASK	GENMASK(16, 8)
2918ac1665SKalle Valo #define TCSR_SOC_HW_VERSION_MINOR_MASK	GENMASK(7, 0)
3018ac1665SKalle Valo 
31a05bd851SCarl Huang /* BAR0 + 4k is always accessible, and no
32a05bd851SCarl Huang  * need to force wakeup.
33a05bd851SCarl Huang  * 4K - 32 = 0xFE0
34a05bd851SCarl Huang  */
35a05bd851SCarl Huang #define ACCESS_ALWAYS_OFF 0xFE0
36a05bd851SCarl Huang 
376e0355afSGovind Singh #define QCA6390_DEVICE_ID		0x1101
386e0355afSGovind Singh 
396e0355afSGovind Singh static const struct pci_device_id ath11k_pci_id_table[] = {
406e0355afSGovind Singh 	{ PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) },
416e0355afSGovind Singh 	{0}
426e0355afSGovind Singh };
436e0355afSGovind Singh 
446e0355afSGovind Singh MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
456e0355afSGovind Singh 
461ff8ed78SGovind Singh static const struct ath11k_bus_params ath11k_pci_bus_params = {
471ff8ed78SGovind Singh 	.mhi_support = true,
4856970454SGovind Singh 	.m3_fw_support = true,
496eb6ea51SGovind Singh 	.fixed_bdf_addr = false,
506eb6ea51SGovind Singh 	.fixed_mem_region = false,
511ff8ed78SGovind Singh };
521ff8ed78SGovind Singh 
535697a564SGovind Singh static const struct ath11k_msi_config msi_config = {
545697a564SGovind Singh 	.total_vectors = 32,
555697a564SGovind Singh 	.total_users = 4,
565697a564SGovind Singh 	.users = (struct ath11k_msi_user[]) {
575697a564SGovind Singh 		{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
585697a564SGovind Singh 		{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
595697a564SGovind Singh 		{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
605697a564SGovind Singh 		{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
615697a564SGovind Singh 	},
625697a564SGovind Singh };
635697a564SGovind Singh 
647f4beda2SGovind Singh static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
657f4beda2SGovind Singh 	"bhi",
667f4beda2SGovind Singh 	"mhi-er0",
677f4beda2SGovind Singh 	"mhi-er1",
687f4beda2SGovind Singh 	"ce0",
697f4beda2SGovind Singh 	"ce1",
707f4beda2SGovind Singh 	"ce2",
717f4beda2SGovind Singh 	"ce3",
727f4beda2SGovind Singh 	"ce4",
737f4beda2SGovind Singh 	"ce5",
747f4beda2SGovind Singh 	"ce6",
757f4beda2SGovind Singh 	"ce7",
767f4beda2SGovind Singh 	"ce8",
777f4beda2SGovind Singh 	"ce9",
787f4beda2SGovind Singh 	"ce10",
797f4beda2SGovind Singh 	"ce11",
807f4beda2SGovind Singh 	"host2wbm-desc-feed",
817f4beda2SGovind Singh 	"host2reo-re-injection",
827f4beda2SGovind Singh 	"host2reo-command",
837f4beda2SGovind Singh 	"host2rxdma-monitor-ring3",
847f4beda2SGovind Singh 	"host2rxdma-monitor-ring2",
857f4beda2SGovind Singh 	"host2rxdma-monitor-ring1",
867f4beda2SGovind Singh 	"reo2ost-exception",
877f4beda2SGovind Singh 	"wbm2host-rx-release",
887f4beda2SGovind Singh 	"reo2host-status",
897f4beda2SGovind Singh 	"reo2host-destination-ring4",
907f4beda2SGovind Singh 	"reo2host-destination-ring3",
917f4beda2SGovind Singh 	"reo2host-destination-ring2",
927f4beda2SGovind Singh 	"reo2host-destination-ring1",
937f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac3",
947f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac2",
957f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac1",
967f4beda2SGovind Singh 	"ppdu-end-interrupts-mac3",
977f4beda2SGovind Singh 	"ppdu-end-interrupts-mac2",
987f4beda2SGovind Singh 	"ppdu-end-interrupts-mac1",
997f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac3",
1007f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac2",
1017f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac1",
1027f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac3",
1037f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac2",
1047f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac1",
1057f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac3",
1067f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac2",
1077f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac1",
1087f4beda2SGovind Singh 	"host2tcl-input-ring4",
1097f4beda2SGovind Singh 	"host2tcl-input-ring3",
1107f4beda2SGovind Singh 	"host2tcl-input-ring2",
1117f4beda2SGovind Singh 	"host2tcl-input-ring1",
1127f4beda2SGovind Singh 	"wbm2host-tx-completions-ring3",
1137f4beda2SGovind Singh 	"wbm2host-tx-completions-ring2",
1147f4beda2SGovind Singh 	"wbm2host-tx-completions-ring1",
1157f4beda2SGovind Singh 	"tcl2host-status-ring",
1167f4beda2SGovind Singh };
1177f4beda2SGovind Singh 
118654e959aSGovind Singh static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset)
119654e959aSGovind Singh {
120654e959aSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
121654e959aSGovind Singh 
122654e959aSGovind Singh 	u32 window = FIELD_GET(WINDOW_VALUE_MASK, offset);
123654e959aSGovind Singh 
124654e959aSGovind Singh 	lockdep_assert_held(&ab_pci->window_lock);
125654e959aSGovind Singh 
126654e959aSGovind Singh 	if (window != ab_pci->register_window) {
127654e959aSGovind Singh 		iowrite32(WINDOW_ENABLE_BIT | window,
128654e959aSGovind Singh 			  ab->mem + WINDOW_REG_ADDRESS);
129654e959aSGovind Singh 		ab_pci->register_window = window;
130654e959aSGovind Singh 	}
131654e959aSGovind Singh }
132654e959aSGovind Singh 
133f3c603d4SCarl Huang void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
134654e959aSGovind Singh {
135654e959aSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
136654e959aSGovind Singh 
137a05bd851SCarl Huang 	/* for offset beyond BAR + 4K - 32, may
138a05bd851SCarl Huang 	 * need to wakeup MHI to access.
139a05bd851SCarl Huang 	 */
140a05bd851SCarl Huang 	if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
141a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
142a05bd851SCarl Huang 		mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
143a05bd851SCarl Huang 
144654e959aSGovind Singh 	if (offset < WINDOW_START) {
145654e959aSGovind Singh 		iowrite32(value, ab->mem  + offset);
146654e959aSGovind Singh 	} else {
147654e959aSGovind Singh 		spin_lock_bh(&ab_pci->window_lock);
148654e959aSGovind Singh 		ath11k_pci_select_window(ab_pci, offset);
149654e959aSGovind Singh 		iowrite32(value, ab->mem + WINDOW_START + (offset & WINDOW_RANGE_MASK));
150654e959aSGovind Singh 		spin_unlock_bh(&ab_pci->window_lock);
151654e959aSGovind Singh 	}
152a05bd851SCarl Huang 
153a05bd851SCarl Huang 	if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
154a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
155a05bd851SCarl Huang 		mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
156654e959aSGovind Singh }
157654e959aSGovind Singh 
158f3c603d4SCarl Huang u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset)
159654e959aSGovind Singh {
160654e959aSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
161654e959aSGovind Singh 	u32 val;
162654e959aSGovind Singh 
163a05bd851SCarl Huang 	/* for offset beyond BAR + 4K - 32, may
164a05bd851SCarl Huang 	 * need to wakeup MHI to access.
165a05bd851SCarl Huang 	 */
166a05bd851SCarl Huang 	if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
167a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
168a05bd851SCarl Huang 		mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
169a05bd851SCarl Huang 
170654e959aSGovind Singh 	if (offset < WINDOW_START) {
171654e959aSGovind Singh 		val = ioread32(ab->mem + offset);
172654e959aSGovind Singh 	} else {
173654e959aSGovind Singh 		spin_lock_bh(&ab_pci->window_lock);
174654e959aSGovind Singh 		ath11k_pci_select_window(ab_pci, offset);
175654e959aSGovind Singh 		val = ioread32(ab->mem + WINDOW_START + (offset & WINDOW_RANGE_MASK));
176654e959aSGovind Singh 		spin_unlock_bh(&ab_pci->window_lock);
177654e959aSGovind Singh 	}
178654e959aSGovind Singh 
179a05bd851SCarl Huang 	if (test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
180a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
181a05bd851SCarl Huang 		mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
182a05bd851SCarl Huang 
183654e959aSGovind Singh 	return val;
184654e959aSGovind Singh }
185654e959aSGovind Singh 
186f3c603d4SCarl Huang static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
187f3c603d4SCarl Huang {
188f3c603d4SCarl Huang 	u32 val, delay;
189f3c603d4SCarl Huang 
190f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
191f3c603d4SCarl Huang 
192f3c603d4SCarl Huang 	val |= PCIE_SOC_GLOBAL_RESET_V;
193f3c603d4SCarl Huang 
194f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
195f3c603d4SCarl Huang 
196f3c603d4SCarl Huang 	/* TODO: exact time to sleep is uncertain */
197f3c603d4SCarl Huang 	delay = 10;
198f3c603d4SCarl Huang 	mdelay(delay);
199f3c603d4SCarl Huang 
200f3c603d4SCarl Huang 	/* Need to toggle V bit back otherwise stuck in reset status */
201f3c603d4SCarl Huang 	val &= ~PCIE_SOC_GLOBAL_RESET_V;
202f3c603d4SCarl Huang 
203f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
204f3c603d4SCarl Huang 
205f3c603d4SCarl Huang 	mdelay(delay);
206f3c603d4SCarl Huang 
207f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
208f3c603d4SCarl Huang 	if (val == 0xffffffff)
209f3c603d4SCarl Huang 		ath11k_warn(ab, "link down error during global reset\n");
210f3c603d4SCarl Huang }
211f3c603d4SCarl Huang 
212f3c603d4SCarl Huang static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
213f3c603d4SCarl Huang {
214f3c603d4SCarl Huang 	u32 val;
215f3c603d4SCarl Huang 
216f3c603d4SCarl Huang 	/* read cookie */
217f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_Q6_COOKIE_ADDR);
218f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "cookie:0x%x\n", val);
219f3c603d4SCarl Huang 
220f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
221f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
222f3c603d4SCarl Huang 
223f3c603d4SCarl Huang 	/* TODO: exact time to sleep is uncertain */
224f3c603d4SCarl Huang 	mdelay(10);
225f3c603d4SCarl Huang 
226f3c603d4SCarl Huang 	/* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from
227f3c603d4SCarl Huang 	 * continuing warm path and entering dead loop.
228f3c603d4SCarl Huang 	 */
229f3c603d4SCarl Huang 	ath11k_pci_write32(ab, WLAON_WARM_SW_ENTRY, 0);
230f3c603d4SCarl Huang 	mdelay(10);
231f3c603d4SCarl Huang 
232f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
233f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
234f3c603d4SCarl Huang 
235f3c603d4SCarl Huang 	/* A read clear register. clear the register to prevent
236f3c603d4SCarl Huang 	 * Q6 from entering wrong code path.
237f3c603d4SCarl Huang 	 */
238f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_SOC_RESET_CAUSE_REG);
239f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val);
240f3c603d4SCarl Huang }
241f3c603d4SCarl Huang 
242f3c603d4SCarl Huang static void ath11k_pci_force_wake(struct ath11k_base *ab)
243f3c603d4SCarl Huang {
244f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1);
245f3c603d4SCarl Huang 	mdelay(5);
246f3c603d4SCarl Huang }
247f3c603d4SCarl Huang 
248f3c603d4SCarl Huang static void ath11k_pci_sw_reset(struct ath11k_base *ab)
249f3c603d4SCarl Huang {
250f3c603d4SCarl Huang 	ath11k_pci_soc_global_reset(ab);
251f3c603d4SCarl Huang 	ath11k_mhi_clear_vector(ab);
252f3c603d4SCarl Huang 	ath11k_pci_soc_global_reset(ab);
253f3c603d4SCarl Huang 	ath11k_mhi_set_mhictrl_reset(ab);
254f3c603d4SCarl Huang 	ath11k_pci_clear_dbg_registers(ab);
255f3c603d4SCarl Huang }
256f3c603d4SCarl Huang 
2571399fb87SGovind Singh int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector)
2581399fb87SGovind Singh {
2591399fb87SGovind Singh 	struct pci_dev *pci_dev = to_pci_dev(dev);
2601399fb87SGovind Singh 
2611399fb87SGovind Singh 	return pci_irq_vector(pci_dev, vector);
2621399fb87SGovind Singh }
2631399fb87SGovind Singh 
264c4eacabeSGovind Singh static void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
265c4eacabeSGovind Singh 				       u32 *msi_addr_hi)
266c4eacabeSGovind Singh {
267c4eacabeSGovind Singh 	struct pci_dev *pci_dev = to_pci_dev(ab->dev);
268c4eacabeSGovind Singh 
269c4eacabeSGovind Singh 	pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO,
270c4eacabeSGovind Singh 			      msi_addr_lo);
271c4eacabeSGovind Singh 
272c4eacabeSGovind Singh 	pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI,
273c4eacabeSGovind Singh 			      msi_addr_hi);
274c4eacabeSGovind Singh }
275c4eacabeSGovind Singh 
2761399fb87SGovind Singh int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name,
2771399fb87SGovind Singh 				       int *num_vectors, u32 *user_base_data,
2781399fb87SGovind Singh 				       u32 *base_vector)
2791399fb87SGovind Singh {
2801399fb87SGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
2811399fb87SGovind Singh 	int idx;
2821399fb87SGovind Singh 
2831399fb87SGovind Singh 	for (idx = 0; idx < msi_config.total_users; idx++) {
2841399fb87SGovind Singh 		if (strcmp(user_name, msi_config.users[idx].name) == 0) {
2851399fb87SGovind Singh 			*num_vectors = msi_config.users[idx].num_vectors;
2861399fb87SGovind Singh 			*user_base_data = msi_config.users[idx].base_vector
2871399fb87SGovind Singh 				+ ab_pci->msi_ep_base_data;
2881399fb87SGovind Singh 			*base_vector = msi_config.users[idx].base_vector;
2891399fb87SGovind Singh 
2901399fb87SGovind Singh 			ath11k_dbg(ab, ATH11K_DBG_PCI, "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
2911399fb87SGovind Singh 				   user_name, *num_vectors, *user_base_data,
2921399fb87SGovind Singh 				   *base_vector);
2931399fb87SGovind Singh 
2941399fb87SGovind Singh 			return 0;
2951399fb87SGovind Singh 		}
2961399fb87SGovind Singh 	}
2971399fb87SGovind Singh 
2981399fb87SGovind Singh 	ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);
2991399fb87SGovind Singh 
3001399fb87SGovind Singh 	return -EINVAL;
3011399fb87SGovind Singh }
3021399fb87SGovind Singh 
303c4eacabeSGovind Singh static int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
304c4eacabeSGovind Singh 					  int *num_vectors, u32 *user_base_data,
305c4eacabeSGovind Singh 					  u32 *base_vector)
306c4eacabeSGovind Singh {
307c4eacabeSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
308c4eacabeSGovind Singh 
309c4eacabeSGovind Singh 	return ath11k_pci_get_user_msi_assignment(ab_pci, user_name,
310c4eacabeSGovind Singh 						  num_vectors, user_base_data,
311c4eacabeSGovind Singh 						  base_vector);
312c4eacabeSGovind Singh }
313c4eacabeSGovind Singh 
314d4ecb90bSCarl Huang static void ath11k_pci_free_ext_irq(struct ath11k_base *ab)
315d4ecb90bSCarl Huang {
316d4ecb90bSCarl Huang 	int i, j;
317d4ecb90bSCarl Huang 
318d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
319d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
320d4ecb90bSCarl Huang 
321d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++)
322d4ecb90bSCarl Huang 			free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
323d4ecb90bSCarl Huang 
324d4ecb90bSCarl Huang 		netif_napi_del(&irq_grp->napi);
325d4ecb90bSCarl Huang 	}
326d4ecb90bSCarl Huang }
327d4ecb90bSCarl Huang 
3287f4beda2SGovind Singh static void ath11k_pci_free_irq(struct ath11k_base *ab)
3297f4beda2SGovind Singh {
3307f4beda2SGovind Singh 	int i, irq_idx;
3317f4beda2SGovind Singh 
332d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
333e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
3347f4beda2SGovind Singh 			continue;
3357f4beda2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
3367f4beda2SGovind Singh 		free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
3377f4beda2SGovind Singh 	}
338d4ecb90bSCarl Huang 
339d4ecb90bSCarl Huang 	ath11k_pci_free_ext_irq(ab);
3407f4beda2SGovind Singh }
3417f4beda2SGovind Singh 
3422c3960c2SGovind Singh static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
3432c3960c2SGovind Singh {
3442c3960c2SGovind Singh 	u32 irq_idx;
3452c3960c2SGovind Singh 
3462c3960c2SGovind Singh 	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
3472c3960c2SGovind Singh 	enable_irq(ab->irq_num[irq_idx]);
3482c3960c2SGovind Singh }
3492c3960c2SGovind Singh 
3507f4beda2SGovind Singh static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
3517f4beda2SGovind Singh {
3527f4beda2SGovind Singh 	u32 irq_idx;
3537f4beda2SGovind Singh 
3547f4beda2SGovind Singh 	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
3557f4beda2SGovind Singh 	disable_irq_nosync(ab->irq_num[irq_idx]);
3567f4beda2SGovind Singh }
3577f4beda2SGovind Singh 
3582c3960c2SGovind Singh static void ath11k_pci_ce_irqs_disable(struct ath11k_base *ab)
3592c3960c2SGovind Singh {
3602c3960c2SGovind Singh 	int i;
3612c3960c2SGovind Singh 
362d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
363e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
3642c3960c2SGovind Singh 			continue;
3652c3960c2SGovind Singh 		ath11k_pci_ce_irq_disable(ab, i);
3662c3960c2SGovind Singh 	}
3672c3960c2SGovind Singh }
3682c3960c2SGovind Singh 
3692c3960c2SGovind Singh static void ath11k_pci_sync_ce_irqs(struct ath11k_base *ab)
3702c3960c2SGovind Singh {
3712c3960c2SGovind Singh 	int i;
3722c3960c2SGovind Singh 	int irq_idx;
3732c3960c2SGovind Singh 
374d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
375e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
3762c3960c2SGovind Singh 			continue;
3772c3960c2SGovind Singh 
3782c3960c2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
3792c3960c2SGovind Singh 		synchronize_irq(ab->irq_num[irq_idx]);
3802c3960c2SGovind Singh 	}
3812c3960c2SGovind Singh }
3822c3960c2SGovind Singh 
3832c3960c2SGovind Singh static void ath11k_pci_ce_tasklet(unsigned long data)
3842c3960c2SGovind Singh {
3852c3960c2SGovind Singh 	struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data;
3862c3960c2SGovind Singh 
3872c3960c2SGovind Singh 	ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
3882c3960c2SGovind Singh 
3892c3960c2SGovind Singh 	ath11k_pci_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num);
3902c3960c2SGovind Singh }
3912c3960c2SGovind Singh 
3927f4beda2SGovind Singh static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
3937f4beda2SGovind Singh {
3947f4beda2SGovind Singh 	struct ath11k_ce_pipe *ce_pipe = arg;
3957f4beda2SGovind Singh 
3967f4beda2SGovind Singh 	ath11k_pci_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num);
3972c3960c2SGovind Singh 	tasklet_schedule(&ce_pipe->intr_tq);
3987f4beda2SGovind Singh 
3997f4beda2SGovind Singh 	return IRQ_HANDLED;
4007f4beda2SGovind Singh }
4017f4beda2SGovind Singh 
402d4ecb90bSCarl Huang static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
403d4ecb90bSCarl Huang {
404d4ecb90bSCarl Huang 	int i;
405d4ecb90bSCarl Huang 
406d4ecb90bSCarl Huang 	for (i = 0; i < irq_grp->num_irq; i++)
407d4ecb90bSCarl Huang 		disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
408d4ecb90bSCarl Huang }
409d4ecb90bSCarl Huang 
410d4ecb90bSCarl Huang static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc)
411d4ecb90bSCarl Huang {
412d4ecb90bSCarl Huang 	int i;
413d4ecb90bSCarl Huang 
414d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
415d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i];
416d4ecb90bSCarl Huang 
417d4ecb90bSCarl Huang 		ath11k_pci_ext_grp_disable(irq_grp);
418d4ecb90bSCarl Huang 
419d4ecb90bSCarl Huang 		napi_synchronize(&irq_grp->napi);
420d4ecb90bSCarl Huang 		napi_disable(&irq_grp->napi);
421d4ecb90bSCarl Huang 	}
422d4ecb90bSCarl Huang }
423d4ecb90bSCarl Huang 
424d4ecb90bSCarl Huang static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
425d4ecb90bSCarl Huang {
426d4ecb90bSCarl Huang 	int i;
427d4ecb90bSCarl Huang 
428d4ecb90bSCarl Huang 	for (i = 0; i < irq_grp->num_irq; i++)
429d4ecb90bSCarl Huang 		enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
430d4ecb90bSCarl Huang }
431d4ecb90bSCarl Huang 
432d4ecb90bSCarl Huang static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab)
433d4ecb90bSCarl Huang {
434d4ecb90bSCarl Huang 	int i;
435d4ecb90bSCarl Huang 
436d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
437d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
438d4ecb90bSCarl Huang 
439d4ecb90bSCarl Huang 		napi_enable(&irq_grp->napi);
440d4ecb90bSCarl Huang 		ath11k_pci_ext_grp_enable(irq_grp);
441d4ecb90bSCarl Huang 	}
442d4ecb90bSCarl Huang }
443d4ecb90bSCarl Huang 
444d4ecb90bSCarl Huang static void ath11k_pci_sync_ext_irqs(struct ath11k_base *ab)
445d4ecb90bSCarl Huang {
446d4ecb90bSCarl Huang 	int i, j, irq_idx;
447d4ecb90bSCarl Huang 
448d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
449d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
450d4ecb90bSCarl Huang 
451d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++) {
452d4ecb90bSCarl Huang 			irq_idx = irq_grp->irqs[j];
453d4ecb90bSCarl Huang 			synchronize_irq(ab->irq_num[irq_idx]);
454d4ecb90bSCarl Huang 		}
455d4ecb90bSCarl Huang 	}
456d4ecb90bSCarl Huang }
457d4ecb90bSCarl Huang 
458d4ecb90bSCarl Huang static void ath11k_pci_ext_irq_disable(struct ath11k_base *ab)
459d4ecb90bSCarl Huang {
460d4ecb90bSCarl Huang 	__ath11k_pci_ext_irq_disable(ab);
461d4ecb90bSCarl Huang 	ath11k_pci_sync_ext_irqs(ab);
462d4ecb90bSCarl Huang }
463d4ecb90bSCarl Huang 
464d4ecb90bSCarl Huang static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget)
465d4ecb90bSCarl Huang {
466d4ecb90bSCarl Huang 	struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
467d4ecb90bSCarl Huang 						struct ath11k_ext_irq_grp,
468d4ecb90bSCarl Huang 						napi);
469d4ecb90bSCarl Huang 	struct ath11k_base *ab = irq_grp->ab;
470d4ecb90bSCarl Huang 	int work_done;
471d4ecb90bSCarl Huang 
472d4ecb90bSCarl Huang 	work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
473d4ecb90bSCarl Huang 	if (work_done < budget) {
474d4ecb90bSCarl Huang 		napi_complete_done(napi, work_done);
475d4ecb90bSCarl Huang 		ath11k_pci_ext_grp_enable(irq_grp);
476d4ecb90bSCarl Huang 	}
477d4ecb90bSCarl Huang 
478d4ecb90bSCarl Huang 	if (work_done > budget)
479d4ecb90bSCarl Huang 		work_done = budget;
480d4ecb90bSCarl Huang 
481d4ecb90bSCarl Huang 	return work_done;
482d4ecb90bSCarl Huang }
483d4ecb90bSCarl Huang 
484d4ecb90bSCarl Huang static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg)
485d4ecb90bSCarl Huang {
486d4ecb90bSCarl Huang 	struct ath11k_ext_irq_grp *irq_grp = arg;
487d4ecb90bSCarl Huang 
488d4ecb90bSCarl Huang 	ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq);
489d4ecb90bSCarl Huang 
490d4ecb90bSCarl Huang 	ath11k_pci_ext_grp_disable(irq_grp);
491d4ecb90bSCarl Huang 
492d4ecb90bSCarl Huang 	napi_schedule(&irq_grp->napi);
493d4ecb90bSCarl Huang 
494d4ecb90bSCarl Huang 	return IRQ_HANDLED;
495d4ecb90bSCarl Huang }
496d4ecb90bSCarl Huang 
497d4ecb90bSCarl Huang static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
498d4ecb90bSCarl Huang {
499d4ecb90bSCarl Huang 	int i, j, ret, num_vectors = 0;
500d4ecb90bSCarl Huang 	u32 user_base_data = 0, base_vector = 0;
501d4ecb90bSCarl Huang 
502b2c09458SColin Ian King 	ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP",
503b2c09458SColin Ian King 						 &num_vectors,
504b2c09458SColin Ian King 						 &user_base_data,
505d4ecb90bSCarl Huang 						 &base_vector);
506b2c09458SColin Ian King 	if (ret < 0)
507b2c09458SColin Ian King 		return ret;
508d4ecb90bSCarl Huang 
509d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
510d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
511d4ecb90bSCarl Huang 		u32 num_irq = 0;
512d4ecb90bSCarl Huang 
513d4ecb90bSCarl Huang 		irq_grp->ab = ab;
514d4ecb90bSCarl Huang 		irq_grp->grp_id = i;
515d4ecb90bSCarl Huang 		init_dummy_netdev(&irq_grp->napi_ndev);
516d4ecb90bSCarl Huang 		netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
517d4ecb90bSCarl Huang 			       ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
518d4ecb90bSCarl Huang 
519d4ecb90bSCarl Huang 		if (ab->hw_params.ring_mask->tx[i] ||
520d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx[i] ||
521d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_err[i] ||
522d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_wbm_rel[i] ||
523d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->reo_status[i] ||
524d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rxdma2host[i] ||
525d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->host2rxdma[i] ||
526d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_mon_status[i]) {
527d4ecb90bSCarl Huang 			num_irq = 1;
528d4ecb90bSCarl Huang 		}
529d4ecb90bSCarl Huang 
530d4ecb90bSCarl Huang 		irq_grp->num_irq = num_irq;
531d4ecb90bSCarl Huang 		irq_grp->irqs[0] = base_vector + i;
532d4ecb90bSCarl Huang 
533d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++) {
534d4ecb90bSCarl Huang 			int irq_idx = irq_grp->irqs[j];
535d4ecb90bSCarl Huang 			int vector = (i % num_vectors) + base_vector;
536d4ecb90bSCarl Huang 			int irq = ath11k_pci_get_msi_irq(ab->dev, vector);
537d4ecb90bSCarl Huang 
538d4ecb90bSCarl Huang 			ab->irq_num[irq_idx] = irq;
539d4ecb90bSCarl Huang 
540d4ecb90bSCarl Huang 			ath11k_dbg(ab, ATH11K_DBG_PCI,
541d4ecb90bSCarl Huang 				   "irq:%d group:%d\n", irq, i);
542d4ecb90bSCarl Huang 			ret = request_irq(irq, ath11k_pci_ext_interrupt_handler,
543d4ecb90bSCarl Huang 					  IRQF_SHARED,
544d4ecb90bSCarl Huang 					  "DP_EXT_IRQ", irq_grp);
545d4ecb90bSCarl Huang 			if (ret) {
546d4ecb90bSCarl Huang 				ath11k_err(ab, "failed request irq %d: %d\n",
547d4ecb90bSCarl Huang 					   vector, ret);
548d4ecb90bSCarl Huang 				return ret;
549d4ecb90bSCarl Huang 			}
550d4ecb90bSCarl Huang 
551d4ecb90bSCarl Huang 			disable_irq_nosync(ab->irq_num[irq_idx]);
552d4ecb90bSCarl Huang 		}
553d4ecb90bSCarl Huang 	}
554d4ecb90bSCarl Huang 
555d4ecb90bSCarl Huang 	return 0;
556d4ecb90bSCarl Huang }
557d4ecb90bSCarl Huang 
5587f4beda2SGovind Singh static int ath11k_pci_config_irq(struct ath11k_base *ab)
5597f4beda2SGovind Singh {
5607f4beda2SGovind Singh 	struct ath11k_ce_pipe *ce_pipe;
5617f4beda2SGovind Singh 	u32 msi_data_start;
5627f4beda2SGovind Singh 	u32 msi_data_count;
5637f4beda2SGovind Singh 	u32 msi_irq_start;
5647f4beda2SGovind Singh 	unsigned int msi_data;
5657f4beda2SGovind Singh 	int irq, i, ret, irq_idx;
5667f4beda2SGovind Singh 
5677f4beda2SGovind Singh 	ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab),
5687f4beda2SGovind Singh 						 "CE", &msi_data_count,
5697f4beda2SGovind Singh 						 &msi_data_start, &msi_irq_start);
5707f4beda2SGovind Singh 	if (ret)
5717f4beda2SGovind Singh 		return ret;
5727f4beda2SGovind Singh 
5737f4beda2SGovind Singh 	/* Configure CE irqs */
574d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
5757f4beda2SGovind Singh 		msi_data = (i % msi_data_count) + msi_irq_start;
5767f4beda2SGovind Singh 		irq = ath11k_pci_get_msi_irq(ab->dev, msi_data);
5777f4beda2SGovind Singh 		ce_pipe = &ab->ce.ce_pipe[i];
5787f4beda2SGovind Singh 
579e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
5807f4beda2SGovind Singh 			continue;
5817f4beda2SGovind Singh 
5827f4beda2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
5837f4beda2SGovind Singh 
5842c3960c2SGovind Singh 		tasklet_init(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet,
5852c3960c2SGovind Singh 			     (unsigned long)ce_pipe);
5862c3960c2SGovind Singh 
5877f4beda2SGovind Singh 		ret = request_irq(irq, ath11k_pci_ce_interrupt_handler,
5887f4beda2SGovind Singh 				  IRQF_SHARED, irq_name[irq_idx],
5897f4beda2SGovind Singh 				  ce_pipe);
5907f4beda2SGovind Singh 		if (ret) {
5917f4beda2SGovind Singh 			ath11k_err(ab, "failed to request irq %d: %d\n",
5927f4beda2SGovind Singh 				   irq_idx, ret);
5937f4beda2SGovind Singh 			return ret;
5947f4beda2SGovind Singh 		}
5957f4beda2SGovind Singh 
5967f4beda2SGovind Singh 		ab->irq_num[irq_idx] = irq;
597e5c860e1SCarl Huang 		ath11k_pci_ce_irq_disable(ab, i);
5987f4beda2SGovind Singh 	}
5997f4beda2SGovind Singh 
600d4ecb90bSCarl Huang 	ret = ath11k_pci_ext_irq_config(ab);
601d4ecb90bSCarl Huang 	if (ret)
602d4ecb90bSCarl Huang 		return ret;
603d4ecb90bSCarl Huang 
6047f4beda2SGovind Singh 	return 0;
6057f4beda2SGovind Singh }
6067f4beda2SGovind Singh 
6077f4beda2SGovind Singh static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
6087f4beda2SGovind Singh {
6097f4beda2SGovind Singh 	struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
6107f4beda2SGovind Singh 
611967c1d11SAnilkumar Kolli 	cfg->tgt_ce = ab->hw_params.target_ce_config;
612967c1d11SAnilkumar Kolli 	cfg->tgt_ce_len = ab->hw_params.target_ce_count;
6137f4beda2SGovind Singh 
614967c1d11SAnilkumar Kolli 	cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map;
615967c1d11SAnilkumar Kolli 	cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len;
616eb8de049SGovind Singh 	ab->qmi.service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390;
617*e838c14aSCarl Huang 
618*e838c14aSCarl Huang 	ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2,
619*e838c14aSCarl Huang 				    &cfg->shadow_reg_v2_len);
6207f4beda2SGovind Singh }
6217f4beda2SGovind Singh 
6227f4beda2SGovind Singh static void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab)
6237f4beda2SGovind Singh {
6247f4beda2SGovind Singh 	int i;
6257f4beda2SGovind Singh 
626d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
627e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
6287f4beda2SGovind Singh 			continue;
6297f4beda2SGovind Singh 		ath11k_pci_ce_irq_enable(ab, i);
6307f4beda2SGovind Singh 	}
6317f4beda2SGovind Singh }
6327f4beda2SGovind Singh 
6335697a564SGovind Singh static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci)
6345697a564SGovind Singh {
6355697a564SGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
6365697a564SGovind Singh 	struct msi_desc *msi_desc;
6375697a564SGovind Singh 	int num_vectors;
6385697a564SGovind Singh 	int ret;
6395697a564SGovind Singh 
6405697a564SGovind Singh 	num_vectors = pci_alloc_irq_vectors(ab_pci->pdev,
6415697a564SGovind Singh 					    msi_config.total_vectors,
6425697a564SGovind Singh 					    msi_config.total_vectors,
6435697a564SGovind Singh 					    PCI_IRQ_MSI);
6445697a564SGovind Singh 	if (num_vectors != msi_config.total_vectors) {
6455697a564SGovind Singh 		ath11k_err(ab, "failed to get %d MSI vectors, only %d available",
6465697a564SGovind Singh 			   msi_config.total_vectors, num_vectors);
6475697a564SGovind Singh 
6485697a564SGovind Singh 		if (num_vectors >= 0)
6495697a564SGovind Singh 			return -EINVAL;
6505697a564SGovind Singh 		else
6515697a564SGovind Singh 			return num_vectors;
6525697a564SGovind Singh 	}
6535697a564SGovind Singh 
6545697a564SGovind Singh 	msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
6555697a564SGovind Singh 	if (!msi_desc) {
6565697a564SGovind Singh 		ath11k_err(ab, "msi_desc is NULL!\n");
6575697a564SGovind Singh 		ret = -EINVAL;
6585697a564SGovind Singh 		goto free_msi_vector;
6595697a564SGovind Singh 	}
6605697a564SGovind Singh 
6615697a564SGovind Singh 	ab_pci->msi_ep_base_data = msi_desc->msg.data;
6625697a564SGovind Singh 
6635697a564SGovind Singh 	ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab_pci->msi_ep_base_data);
6645697a564SGovind Singh 
6655697a564SGovind Singh 	return 0;
6665697a564SGovind Singh 
6675697a564SGovind Singh free_msi_vector:
6685697a564SGovind Singh 	pci_free_irq_vectors(ab_pci->pdev);
6695697a564SGovind Singh 
6705697a564SGovind Singh 	return ret;
6715697a564SGovind Singh }
6725697a564SGovind Singh 
6735697a564SGovind Singh static void ath11k_pci_disable_msi(struct ath11k_pci *ab_pci)
6745697a564SGovind Singh {
6755697a564SGovind Singh 	pci_free_irq_vectors(ab_pci->pdev);
6765697a564SGovind Singh }
6775697a564SGovind Singh 
6785762613eSGovind Singh static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev)
6795762613eSGovind Singh {
6805762613eSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
6815762613eSGovind Singh 	u16 device_id;
6825762613eSGovind Singh 	int ret = 0;
6835762613eSGovind Singh 
6845762613eSGovind Singh 	pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
6855762613eSGovind Singh 	if (device_id != ab_pci->dev_id)  {
6865762613eSGovind Singh 		ath11k_err(ab, "pci device id mismatch: 0x%x 0x%x\n",
6875762613eSGovind Singh 			   device_id, ab_pci->dev_id);
6885762613eSGovind Singh 		ret = -EIO;
6895762613eSGovind Singh 		goto out;
6905762613eSGovind Singh 	}
6915762613eSGovind Singh 
6925762613eSGovind Singh 	ret = pci_assign_resource(pdev, ATH11K_PCI_BAR_NUM);
6935762613eSGovind Singh 	if (ret) {
6945762613eSGovind Singh 		ath11k_err(ab, "failed to assign pci resource: %d\n", ret);
6955762613eSGovind Singh 		goto out;
6965762613eSGovind Singh 	}
6975762613eSGovind Singh 
6985762613eSGovind Singh 	ret = pci_enable_device(pdev);
6995762613eSGovind Singh 	if (ret) {
7005762613eSGovind Singh 		ath11k_err(ab, "failed to enable pci device: %d\n", ret);
7015762613eSGovind Singh 		goto out;
7025762613eSGovind Singh 	}
7035762613eSGovind Singh 
7045762613eSGovind Singh 	ret = pci_request_region(pdev, ATH11K_PCI_BAR_NUM, "ath11k_pci");
7055762613eSGovind Singh 	if (ret) {
7065762613eSGovind Singh 		ath11k_err(ab, "failed to request pci region: %d\n", ret);
7075762613eSGovind Singh 		goto disable_device;
7085762613eSGovind Singh 	}
7095762613eSGovind Singh 
7105762613eSGovind Singh 	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(ATH11K_PCI_DMA_MASK));
7115762613eSGovind Singh 	if (ret) {
7125762613eSGovind Singh 		ath11k_err(ab, "failed to set pci dma mask to %d: %d\n",
7135762613eSGovind Singh 			   ATH11K_PCI_DMA_MASK, ret);
7145762613eSGovind Singh 		goto release_region;
7155762613eSGovind Singh 	}
7165762613eSGovind Singh 
7175762613eSGovind Singh 	ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(ATH11K_PCI_DMA_MASK));
7185762613eSGovind Singh 	if (ret) {
7195762613eSGovind Singh 		ath11k_err(ab, "failed to set pci consistent dma mask to %d: %d\n",
7205762613eSGovind Singh 			   ATH11K_PCI_DMA_MASK, ret);
7215762613eSGovind Singh 		goto release_region;
7225762613eSGovind Singh 	}
7235762613eSGovind Singh 
7245762613eSGovind Singh 	pci_set_master(pdev);
7255762613eSGovind Singh 
7265762613eSGovind Singh 	ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM);
7275762613eSGovind Singh 	ab->mem = pci_iomap(pdev, ATH11K_PCI_BAR_NUM, 0);
7285762613eSGovind Singh 	if (!ab->mem) {
7295762613eSGovind Singh 		ath11k_err(ab, "failed to map pci bar %d\n", ATH11K_PCI_BAR_NUM);
7305762613eSGovind Singh 		ret = -EIO;
7315762613eSGovind Singh 		goto clear_master;
7325762613eSGovind Singh 	}
7335762613eSGovind Singh 
7345762613eSGovind Singh 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem);
7355762613eSGovind Singh 	return 0;
7365762613eSGovind Singh 
7375762613eSGovind Singh clear_master:
7385762613eSGovind Singh 	pci_clear_master(pdev);
7395762613eSGovind Singh release_region:
7405762613eSGovind Singh 	pci_release_region(pdev, ATH11K_PCI_BAR_NUM);
7415762613eSGovind Singh disable_device:
7425762613eSGovind Singh 	pci_disable_device(pdev);
7435762613eSGovind Singh out:
7445762613eSGovind Singh 	return ret;
7455762613eSGovind Singh }
7465762613eSGovind Singh 
7475762613eSGovind Singh static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
7485762613eSGovind Singh {
7495762613eSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
7505762613eSGovind Singh 	struct pci_dev *pci_dev = ab_pci->pdev;
7515762613eSGovind Singh 
7525762613eSGovind Singh 	pci_iounmap(pci_dev, ab->mem);
7535762613eSGovind Singh 	ab->mem = NULL;
7545762613eSGovind Singh 	pci_clear_master(pci_dev);
7555762613eSGovind Singh 	pci_release_region(pci_dev, ATH11K_PCI_BAR_NUM);
7565762613eSGovind Singh 	if (pci_is_enabled(pci_dev))
7575762613eSGovind Singh 		pci_disable_device(pci_dev);
7585762613eSGovind Singh }
7595762613eSGovind Singh 
7601399fb87SGovind Singh static int ath11k_pci_power_up(struct ath11k_base *ab)
7611399fb87SGovind Singh {
7621399fb87SGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
7631399fb87SGovind Singh 	int ret;
7641399fb87SGovind Singh 
765a05bd851SCarl Huang 	ab_pci->register_window = 0;
766a05bd851SCarl Huang 	clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
767f3c603d4SCarl Huang 	ath11k_pci_sw_reset(ab_pci->ab);
768f3c603d4SCarl Huang 
7691399fb87SGovind Singh 	ret = ath11k_mhi_start(ab_pci);
7701399fb87SGovind Singh 	if (ret) {
7711399fb87SGovind Singh 		ath11k_err(ab, "failed to start mhi: %d\n", ret);
7721399fb87SGovind Singh 		return ret;
7731399fb87SGovind Singh 	}
7741399fb87SGovind Singh 
7751399fb87SGovind Singh 	return 0;
7761399fb87SGovind Singh }
7771399fb87SGovind Singh 
7781399fb87SGovind Singh static void ath11k_pci_power_down(struct ath11k_base *ab)
7791399fb87SGovind Singh {
7801399fb87SGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
7811399fb87SGovind Singh 
7821399fb87SGovind Singh 	ath11k_mhi_stop(ab_pci);
783a05bd851SCarl Huang 	clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
784f3c603d4SCarl Huang 	ath11k_pci_force_wake(ab_pci->ab);
785f3c603d4SCarl Huang 	ath11k_pci_sw_reset(ab_pci->ab);
7861399fb87SGovind Singh }
7871399fb87SGovind Singh 
7882c3960c2SGovind Singh static void ath11k_pci_kill_tasklets(struct ath11k_base *ab)
7892c3960c2SGovind Singh {
7902c3960c2SGovind Singh 	int i;
7912c3960c2SGovind Singh 
792d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
7932c3960c2SGovind Singh 		struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
7942c3960c2SGovind Singh 
795e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
7962c3960c2SGovind Singh 			continue;
7972c3960c2SGovind Singh 
7982c3960c2SGovind Singh 		tasklet_kill(&ce_pipe->intr_tq);
7992c3960c2SGovind Singh 	}
8002c3960c2SGovind Singh }
8012c3960c2SGovind Singh 
8027f4beda2SGovind Singh static void ath11k_pci_stop(struct ath11k_base *ab)
8037f4beda2SGovind Singh {
8042c3960c2SGovind Singh 	ath11k_pci_ce_irqs_disable(ab);
8052c3960c2SGovind Singh 	ath11k_pci_sync_ce_irqs(ab);
8062c3960c2SGovind Singh 	ath11k_pci_kill_tasklets(ab);
8077f4beda2SGovind Singh 	ath11k_ce_cleanup_pipes(ab);
8087f4beda2SGovind Singh }
8097f4beda2SGovind Singh 
8107f4beda2SGovind Singh static int ath11k_pci_start(struct ath11k_base *ab)
8117f4beda2SGovind Singh {
812a05bd851SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
813a05bd851SCarl Huang 
814a05bd851SCarl Huang 	set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
815a05bd851SCarl Huang 
8167f4beda2SGovind Singh 	ath11k_pci_ce_irqs_enable(ab);
8172c3960c2SGovind Singh 	ath11k_ce_rx_post_buf(ab);
8182c3960c2SGovind Singh 
8192c3960c2SGovind Singh 	return 0;
8202c3960c2SGovind Singh }
8212c3960c2SGovind Singh 
8222c3960c2SGovind Singh static int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
8232c3960c2SGovind Singh 					  u8 *ul_pipe, u8 *dl_pipe)
8242c3960c2SGovind Singh {
8252c3960c2SGovind Singh 	const struct service_to_pipe *entry;
8262c3960c2SGovind Singh 	bool ul_set = false, dl_set = false;
8272c3960c2SGovind Singh 	int i;
8282c3960c2SGovind Singh 
829967c1d11SAnilkumar Kolli 	for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {
830967c1d11SAnilkumar Kolli 		entry = &ab->hw_params.svc_to_ce_map[i];
8312c3960c2SGovind Singh 
8322c3960c2SGovind Singh 		if (__le32_to_cpu(entry->service_id) != service_id)
8332c3960c2SGovind Singh 			continue;
8342c3960c2SGovind Singh 
8352c3960c2SGovind Singh 		switch (__le32_to_cpu(entry->pipedir)) {
8362c3960c2SGovind Singh 		case PIPEDIR_NONE:
8372c3960c2SGovind Singh 			break;
8382c3960c2SGovind Singh 		case PIPEDIR_IN:
8392c3960c2SGovind Singh 			WARN_ON(dl_set);
8402c3960c2SGovind Singh 			*dl_pipe = __le32_to_cpu(entry->pipenum);
8412c3960c2SGovind Singh 			dl_set = true;
8422c3960c2SGovind Singh 			break;
8432c3960c2SGovind Singh 		case PIPEDIR_OUT:
8442c3960c2SGovind Singh 			WARN_ON(ul_set);
8452c3960c2SGovind Singh 			*ul_pipe = __le32_to_cpu(entry->pipenum);
8462c3960c2SGovind Singh 			ul_set = true;
8472c3960c2SGovind Singh 			break;
8482c3960c2SGovind Singh 		case PIPEDIR_INOUT:
8492c3960c2SGovind Singh 			WARN_ON(dl_set);
8502c3960c2SGovind Singh 			WARN_ON(ul_set);
8512c3960c2SGovind Singh 			*dl_pipe = __le32_to_cpu(entry->pipenum);
8522c3960c2SGovind Singh 			*ul_pipe = __le32_to_cpu(entry->pipenum);
8532c3960c2SGovind Singh 			dl_set = true;
8542c3960c2SGovind Singh 			ul_set = true;
8552c3960c2SGovind Singh 			break;
8562c3960c2SGovind Singh 		}
8572c3960c2SGovind Singh 	}
8582c3960c2SGovind Singh 
8592c3960c2SGovind Singh 	if (WARN_ON(!ul_set || !dl_set))
8602c3960c2SGovind Singh 		return -ENOENT;
8617f4beda2SGovind Singh 
8627f4beda2SGovind Singh 	return 0;
8637f4beda2SGovind Singh }
8647f4beda2SGovind Singh 
8657f4beda2SGovind Singh static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
8667f4beda2SGovind Singh 	.start = ath11k_pci_start,
8677f4beda2SGovind Singh 	.stop = ath11k_pci_stop,
868654e959aSGovind Singh 	.read32 = ath11k_pci_read32,
869654e959aSGovind Singh 	.write32 = ath11k_pci_write32,
8701399fb87SGovind Singh 	.power_down = ath11k_pci_power_down,
8711399fb87SGovind Singh 	.power_up = ath11k_pci_power_up,
872d4ecb90bSCarl Huang 	.irq_enable = ath11k_pci_ext_irq_enable,
873d4ecb90bSCarl Huang 	.irq_disable = ath11k_pci_ext_irq_disable,
874c4eacabeSGovind Singh 	.get_msi_address =  ath11k_pci_get_msi_address,
875c4eacabeSGovind Singh 	.get_user_msi_vector = ath11k_get_user_msi_assignment,
8762c3960c2SGovind Singh 	.map_service_to_pipe = ath11k_pci_map_service_to_pipe,
8771399fb87SGovind Singh };
8781399fb87SGovind Singh 
8796e0355afSGovind Singh static int ath11k_pci_probe(struct pci_dev *pdev,
8806e0355afSGovind Singh 			    const struct pci_device_id *pci_dev)
8816e0355afSGovind Singh {
8826e0355afSGovind Singh 	struct ath11k_base *ab;
8835762613eSGovind Singh 	struct ath11k_pci *ab_pci;
88418ac1665SKalle Valo 	u32 soc_hw_version, soc_hw_version_major, soc_hw_version_minor;
8855762613eSGovind Singh 	int ret;
8866e0355afSGovind Singh 
8876e0355afSGovind Singh 	dev_warn(&pdev->dev, "WARNING: ath11k PCI support is experimental!\n");
8886e0355afSGovind Singh 
8891ff8ed78SGovind Singh 	ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI,
8901ff8ed78SGovind Singh 			       &ath11k_pci_bus_params);
8916e0355afSGovind Singh 	if (!ab) {
8926e0355afSGovind Singh 		dev_err(&pdev->dev, "failed to allocate ath11k base\n");
8936e0355afSGovind Singh 		return -ENOMEM;
8946e0355afSGovind Singh 	}
8956e0355afSGovind Singh 
8966e0355afSGovind Singh 	ab->dev = &pdev->dev;
8976e0355afSGovind Singh 	pci_set_drvdata(pdev, ab);
8985762613eSGovind Singh 	ab_pci = ath11k_pci_priv(ab);
8995762613eSGovind Singh 	ab_pci->dev_id = pci_dev->device;
9005762613eSGovind Singh 	ab_pci->ab = ab;
9015697a564SGovind Singh 	ab_pci->pdev = pdev;
9027f4beda2SGovind Singh 	ab->hif.ops = &ath11k_pci_hif_ops;
9035762613eSGovind Singh 	pci_set_drvdata(pdev, ab);
904654e959aSGovind Singh 	spin_lock_init(&ab_pci->window_lock);
9055762613eSGovind Singh 
9065762613eSGovind Singh 	ret = ath11k_pci_claim(ab_pci, pdev);
9075762613eSGovind Singh 	if (ret) {
9085762613eSGovind Singh 		ath11k_err(ab, "failed to claim device: %d\n", ret);
9095762613eSGovind Singh 		goto err_free_core;
9105762613eSGovind Singh 	}
9116e0355afSGovind Singh 
91218ac1665SKalle Valo 	switch (pci_dev->device) {
91318ac1665SKalle Valo 	case QCA6390_DEVICE_ID:
91418ac1665SKalle Valo 		soc_hw_version = ath11k_pci_read32(ab, TCSR_SOC_HW_VERSION);
91518ac1665SKalle Valo 		soc_hw_version_major = FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK,
91618ac1665SKalle Valo 						 soc_hw_version);
91718ac1665SKalle Valo 		soc_hw_version_minor = FIELD_GET(TCSR_SOC_HW_VERSION_MINOR_MASK,
91818ac1665SKalle Valo 						 soc_hw_version);
91918ac1665SKalle Valo 
92018ac1665SKalle Valo 		ath11k_dbg(ab, ATH11K_DBG_PCI, "pci tcsr_soc_hw_version major %d minor %d\n",
92118ac1665SKalle Valo 			   soc_hw_version_major, soc_hw_version_minor);
92218ac1665SKalle Valo 
92318ac1665SKalle Valo 		switch (soc_hw_version_major) {
92418ac1665SKalle Valo 		case 2:
92518ac1665SKalle Valo 			ab->hw_rev = ATH11K_HW_QCA6390_HW20;
92618ac1665SKalle Valo 			break;
92718ac1665SKalle Valo 		default:
92818ac1665SKalle Valo 			dev_err(&pdev->dev, "Unsupported QCA6390 SOC hardware version: %d %d\n",
92918ac1665SKalle Valo 				soc_hw_version_major, soc_hw_version_minor);
93018ac1665SKalle Valo 			ret = -EOPNOTSUPP;
93118ac1665SKalle Valo 			goto err_pci_free_region;
93218ac1665SKalle Valo 		}
93318ac1665SKalle Valo 		break;
93418ac1665SKalle Valo 	default:
93518ac1665SKalle Valo 		dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n",
93618ac1665SKalle Valo 			pci_dev->device);
93718ac1665SKalle Valo 		ret = -EOPNOTSUPP;
93818ac1665SKalle Valo 		goto err_pci_free_region;
93918ac1665SKalle Valo 	}
94018ac1665SKalle Valo 
9415697a564SGovind Singh 	ret = ath11k_pci_enable_msi(ab_pci);
9425697a564SGovind Singh 	if (ret) {
9435697a564SGovind Singh 		ath11k_err(ab, "failed to enable msi: %d\n", ret);
9445697a564SGovind Singh 		goto err_pci_free_region;
9455697a564SGovind Singh 	}
9465697a564SGovind Singh 
947b8246f88SKalle Valo 	ret = ath11k_core_pre_init(ab);
948b8246f88SKalle Valo 	if (ret)
949b8246f88SKalle Valo 		goto err_pci_disable_msi;
950b8246f88SKalle Valo 
9511399fb87SGovind Singh 	ret = ath11k_mhi_register(ab_pci);
9521399fb87SGovind Singh 	if (ret) {
9531399fb87SGovind Singh 		ath11k_err(ab, "failed to register mhi: %d\n", ret);
9541399fb87SGovind Singh 		goto err_pci_disable_msi;
9551399fb87SGovind Singh 	}
9561399fb87SGovind Singh 
9577f4beda2SGovind Singh 	ret = ath11k_hal_srng_init(ab);
9587f4beda2SGovind Singh 	if (ret)
9597f4beda2SGovind Singh 		goto err_mhi_unregister;
9607f4beda2SGovind Singh 
9617f4beda2SGovind Singh 	ret = ath11k_ce_alloc_pipes(ab);
9627f4beda2SGovind Singh 	if (ret) {
9637f4beda2SGovind Singh 		ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret);
9647f4beda2SGovind Singh 		goto err_hal_srng_deinit;
9657f4beda2SGovind Singh 	}
9667f4beda2SGovind Singh 
9677f4beda2SGovind Singh 	ath11k_pci_init_qmi_ce_config(ab);
9687f4beda2SGovind Singh 
9697f4beda2SGovind Singh 	ret = ath11k_pci_config_irq(ab);
9707f4beda2SGovind Singh 	if (ret) {
9717f4beda2SGovind Singh 		ath11k_err(ab, "failed to config irq: %d\n", ret);
9727f4beda2SGovind Singh 		goto err_ce_free;
9737f4beda2SGovind Singh 	}
9747f4beda2SGovind Singh 
9757f4beda2SGovind Singh 	ret = ath11k_core_init(ab);
9767f4beda2SGovind Singh 	if (ret) {
9777f4beda2SGovind Singh 		ath11k_err(ab, "failed to init core: %d\n", ret);
9787f4beda2SGovind Singh 		goto err_free_irq;
9797f4beda2SGovind Singh 	}
9806e0355afSGovind Singh 	return 0;
9815762613eSGovind Singh 
9827f4beda2SGovind Singh err_free_irq:
9837f4beda2SGovind Singh 	ath11k_pci_free_irq(ab);
9847f4beda2SGovind Singh 
9857f4beda2SGovind Singh err_ce_free:
9867f4beda2SGovind Singh 	ath11k_ce_free_pipes(ab);
9877f4beda2SGovind Singh 
9887f4beda2SGovind Singh err_hal_srng_deinit:
9897f4beda2SGovind Singh 	ath11k_hal_srng_deinit(ab);
9907f4beda2SGovind Singh 
9917f4beda2SGovind Singh err_mhi_unregister:
9927f4beda2SGovind Singh 	ath11k_mhi_unregister(ab_pci);
9937f4beda2SGovind Singh 
994b8246f88SKalle Valo err_pci_disable_msi:
995b8246f88SKalle Valo 	ath11k_pci_disable_msi(ab_pci);
996b8246f88SKalle Valo 
9975697a564SGovind Singh err_pci_free_region:
9985697a564SGovind Singh 	ath11k_pci_free_region(ab_pci);
9995697a564SGovind Singh 
10005762613eSGovind Singh err_free_core:
10015762613eSGovind Singh 	ath11k_core_free(ab);
10025697a564SGovind Singh 
10035762613eSGovind Singh 	return ret;
10046e0355afSGovind Singh }
10056e0355afSGovind Singh 
10066e0355afSGovind Singh static void ath11k_pci_remove(struct pci_dev *pdev)
10076e0355afSGovind Singh {
10086e0355afSGovind Singh 	struct ath11k_base *ab = pci_get_drvdata(pdev);
10095762613eSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
10106e0355afSGovind Singh 
10116e0355afSGovind Singh 	set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
10126fbd8898SCarl Huang 
10136fbd8898SCarl Huang 	ath11k_core_deinit(ab);
10146fbd8898SCarl Huang 
10151399fb87SGovind Singh 	ath11k_mhi_unregister(ab_pci);
10166fbd8898SCarl Huang 
10176fbd8898SCarl Huang 	ath11k_pci_free_irq(ab);
10185697a564SGovind Singh 	ath11k_pci_disable_msi(ab_pci);
10195762613eSGovind Singh 	ath11k_pci_free_region(ab_pci);
10206fbd8898SCarl Huang 
10216fbd8898SCarl Huang 	ath11k_hal_srng_deinit(ab);
10226fbd8898SCarl Huang 	ath11k_ce_free_pipes(ab);
10236e0355afSGovind Singh 	ath11k_core_free(ab);
10246e0355afSGovind Singh }
10256e0355afSGovind Singh 
10261399fb87SGovind Singh static void ath11k_pci_shutdown(struct pci_dev *pdev)
10271399fb87SGovind Singh {
10281399fb87SGovind Singh 	struct ath11k_base *ab = pci_get_drvdata(pdev);
10291399fb87SGovind Singh 
10301399fb87SGovind Singh 	ath11k_pci_power_down(ab);
10311399fb87SGovind Singh }
10321399fb87SGovind Singh 
10336e0355afSGovind Singh static struct pci_driver ath11k_pci_driver = {
10346e0355afSGovind Singh 	.name = "ath11k_pci",
10356e0355afSGovind Singh 	.id_table = ath11k_pci_id_table,
10366e0355afSGovind Singh 	.probe = ath11k_pci_probe,
10376e0355afSGovind Singh 	.remove = ath11k_pci_remove,
10381399fb87SGovind Singh 	.shutdown = ath11k_pci_shutdown,
10396e0355afSGovind Singh };
10406e0355afSGovind Singh 
10416e0355afSGovind Singh static int ath11k_pci_init(void)
10426e0355afSGovind Singh {
10436e0355afSGovind Singh 	int ret;
10446e0355afSGovind Singh 
10456e0355afSGovind Singh 	ret = pci_register_driver(&ath11k_pci_driver);
10466e0355afSGovind Singh 	if (ret)
10476e0355afSGovind Singh 		pr_err("failed to register ath11k pci driver: %d\n",
10486e0355afSGovind Singh 		       ret);
10496e0355afSGovind Singh 
10506e0355afSGovind Singh 	return ret;
10516e0355afSGovind Singh }
10526e0355afSGovind Singh module_init(ath11k_pci_init);
10536e0355afSGovind Singh 
10546e0355afSGovind Singh static void ath11k_pci_exit(void)
10556e0355afSGovind Singh {
10566e0355afSGovind Singh 	pci_unregister_driver(&ath11k_pci_driver);
10576e0355afSGovind Singh }
10586e0355afSGovind Singh 
10596e0355afSGovind Singh module_exit(ath11k_pci_exit);
10606e0355afSGovind Singh 
10616e0355afSGovind Singh MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN PCIe devices");
10626e0355afSGovind Singh MODULE_LICENSE("Dual BSD/GPL");
1063