xref: /openbmc/linux/drivers/net/wireless/ath/ath11k/pci.c (revision b4f4c56459a5c744f7f066b9fc2b54ea995030c5)
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>
96ac04bdcSAnilkumar Kolli #include <linux/of.h>
106e0355afSGovind Singh 
115762613eSGovind Singh #include "pci.h"
126e0355afSGovind Singh #include "core.h"
131399fb87SGovind Singh #include "hif.h"
141399fb87SGovind Singh #include "mhi.h"
156e0355afSGovind Singh #include "debug.h"
166e0355afSGovind Singh 
175762613eSGovind Singh #define ATH11K_PCI_BAR_NUM		0
185762613eSGovind Singh #define ATH11K_PCI_DMA_MASK		32
195762613eSGovind Singh 
207f4beda2SGovind Singh #define ATH11K_PCI_IRQ_CE0_OFFSET	3
214ab4693fSCarl Huang #define ATH11K_PCI_IRQ_DP_OFFSET	14
227f4beda2SGovind Singh 
23654e959aSGovind Singh #define WINDOW_ENABLE_BIT		0x40000000
24654e959aSGovind Singh #define WINDOW_REG_ADDRESS		0x310c
25654e959aSGovind Singh #define WINDOW_VALUE_MASK		GENMASK(24, 19)
26654e959aSGovind Singh #define WINDOW_START			0x80000
27654e959aSGovind Singh #define WINDOW_RANGE_MASK		GENMASK(18, 0)
28654e959aSGovind Singh 
2918ac1665SKalle Valo #define TCSR_SOC_HW_VERSION		0x0224
30d1147a31SBaochen Qiang #define TCSR_SOC_HW_VERSION_MAJOR_MASK	GENMASK(11, 8)
3118ac1665SKalle Valo #define TCSR_SOC_HW_VERSION_MINOR_MASK	GENMASK(7, 0)
3218ac1665SKalle Valo 
33a05bd851SCarl Huang /* BAR0 + 4k is always accessible, and no
34a05bd851SCarl Huang  * need to force wakeup.
35a05bd851SCarl Huang  * 4K - 32 = 0xFE0
36a05bd851SCarl Huang  */
37a05bd851SCarl Huang #define ACCESS_ALWAYS_OFF 0xFE0
38a05bd851SCarl Huang 
396e0355afSGovind Singh #define QCA6390_DEVICE_ID		0x1101
404e809461SAnilkumar Kolli #define QCN9074_DEVICE_ID		0x1104
410fbf1957SBaochen Qiang #define WCN6855_DEVICE_ID		0x1103
426e0355afSGovind Singh 
436e0355afSGovind Singh static const struct pci_device_id ath11k_pci_id_table[] = {
446e0355afSGovind Singh 	{ PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) },
450fbf1957SBaochen Qiang 	{ PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) },
4649f5b114SAnilkumar Kolli 	{ PCI_VDEVICE(QCOM, QCN9074_DEVICE_ID) },
476e0355afSGovind Singh 	{0}
486e0355afSGovind Singh };
496e0355afSGovind Singh 
506e0355afSGovind Singh MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
516e0355afSGovind Singh 
521ff8ed78SGovind Singh static const struct ath11k_bus_params ath11k_pci_bus_params = {
531ff8ed78SGovind Singh 	.mhi_support = true,
5456970454SGovind Singh 	.m3_fw_support = true,
556eb6ea51SGovind Singh 	.fixed_bdf_addr = false,
566eb6ea51SGovind Singh 	.fixed_mem_region = false,
571ff8ed78SGovind Singh };
581ff8ed78SGovind Singh 
597a3aed0cSAnilkumar Kolli static const struct ath11k_msi_config ath11k_msi_config[] = {
607a3aed0cSAnilkumar Kolli 	{
615697a564SGovind Singh 		.total_vectors = 32,
625697a564SGovind Singh 		.total_users = 4,
635697a564SGovind Singh 		.users = (struct ath11k_msi_user[]) {
645697a564SGovind Singh 			{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
655697a564SGovind Singh 			{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
665697a564SGovind Singh 			{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
675697a564SGovind Singh 			{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
685697a564SGovind Singh 		},
697a3aed0cSAnilkumar Kolli 	},
704e809461SAnilkumar Kolli 	{
714e809461SAnilkumar Kolli 		.total_vectors = 16,
724e809461SAnilkumar Kolli 		.total_users = 3,
734e809461SAnilkumar Kolli 		.users = (struct ath11k_msi_user[]) {
744e809461SAnilkumar Kolli 			{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
754e809461SAnilkumar Kolli 			{ .name = "CE", .num_vectors = 5, .base_vector = 3 },
764e809461SAnilkumar Kolli 			{ .name = "DP", .num_vectors = 8, .base_vector = 8 },
774e809461SAnilkumar Kolli 		},
784e809461SAnilkumar Kolli 	},
795697a564SGovind Singh };
805697a564SGovind Singh 
81ac6e7348SCarl Huang static const struct ath11k_msi_config msi_config_one_msi = {
82ac6e7348SCarl Huang 	.total_vectors = 1,
83ac6e7348SCarl Huang 	.total_users = 4,
84ac6e7348SCarl Huang 	.users = (struct ath11k_msi_user[]) {
85ac6e7348SCarl Huang 		{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
86ac6e7348SCarl Huang 		{ .name = "CE", .num_vectors = 1, .base_vector = 0 },
87ac6e7348SCarl Huang 		{ .name = "WAKE", .num_vectors = 1, .base_vector = 0 },
88ac6e7348SCarl Huang 		{ .name = "DP", .num_vectors = 1, .base_vector = 0 },
89ac6e7348SCarl Huang 	},
90ac6e7348SCarl Huang };
91ac6e7348SCarl Huang 
927f4beda2SGovind Singh static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
937f4beda2SGovind Singh 	"bhi",
947f4beda2SGovind Singh 	"mhi-er0",
957f4beda2SGovind Singh 	"mhi-er1",
967f4beda2SGovind Singh 	"ce0",
977f4beda2SGovind Singh 	"ce1",
987f4beda2SGovind Singh 	"ce2",
997f4beda2SGovind Singh 	"ce3",
1007f4beda2SGovind Singh 	"ce4",
1017f4beda2SGovind Singh 	"ce5",
1027f4beda2SGovind Singh 	"ce6",
1037f4beda2SGovind Singh 	"ce7",
1047f4beda2SGovind Singh 	"ce8",
1057f4beda2SGovind Singh 	"ce9",
1067f4beda2SGovind Singh 	"ce10",
1077f4beda2SGovind Singh 	"ce11",
1087f4beda2SGovind Singh 	"host2wbm-desc-feed",
1097f4beda2SGovind Singh 	"host2reo-re-injection",
1107f4beda2SGovind Singh 	"host2reo-command",
1117f4beda2SGovind Singh 	"host2rxdma-monitor-ring3",
1127f4beda2SGovind Singh 	"host2rxdma-monitor-ring2",
1137f4beda2SGovind Singh 	"host2rxdma-monitor-ring1",
1147f4beda2SGovind Singh 	"reo2ost-exception",
1157f4beda2SGovind Singh 	"wbm2host-rx-release",
1167f4beda2SGovind Singh 	"reo2host-status",
1177f4beda2SGovind Singh 	"reo2host-destination-ring4",
1187f4beda2SGovind Singh 	"reo2host-destination-ring3",
1197f4beda2SGovind Singh 	"reo2host-destination-ring2",
1207f4beda2SGovind Singh 	"reo2host-destination-ring1",
1217f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac3",
1227f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac2",
1237f4beda2SGovind Singh 	"rxdma2host-monitor-destination-mac1",
1247f4beda2SGovind Singh 	"ppdu-end-interrupts-mac3",
1257f4beda2SGovind Singh 	"ppdu-end-interrupts-mac2",
1267f4beda2SGovind Singh 	"ppdu-end-interrupts-mac1",
1277f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac3",
1287f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac2",
1297f4beda2SGovind Singh 	"rxdma2host-monitor-status-ring-mac1",
1307f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac3",
1317f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac2",
1327f4beda2SGovind Singh 	"host2rxdma-host-buf-ring-mac1",
1337f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac3",
1347f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac2",
1357f4beda2SGovind Singh 	"rxdma2host-destination-ring-mac1",
1367f4beda2SGovind Singh 	"host2tcl-input-ring4",
1377f4beda2SGovind Singh 	"host2tcl-input-ring3",
1387f4beda2SGovind Singh 	"host2tcl-input-ring2",
1397f4beda2SGovind Singh 	"host2tcl-input-ring1",
1407f4beda2SGovind Singh 	"wbm2host-tx-completions-ring3",
1417f4beda2SGovind Singh 	"wbm2host-tx-completions-ring2",
1427f4beda2SGovind Singh 	"wbm2host-tx-completions-ring1",
1437f4beda2SGovind Singh 	"tcl2host-status-ring",
1447f4beda2SGovind Singh };
1457f4beda2SGovind Singh 
146654e959aSGovind Singh static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset)
147654e959aSGovind Singh {
148654e959aSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
149654e959aSGovind Singh 
150654e959aSGovind Singh 	u32 window = FIELD_GET(WINDOW_VALUE_MASK, offset);
151654e959aSGovind Singh 
152654e959aSGovind Singh 	lockdep_assert_held(&ab_pci->window_lock);
153654e959aSGovind Singh 
154654e959aSGovind Singh 	if (window != ab_pci->register_window) {
155654e959aSGovind Singh 		iowrite32(WINDOW_ENABLE_BIT | window,
156654e959aSGovind Singh 			  ab->mem + WINDOW_REG_ADDRESS);
157f6fa37a4SCarl Huang 		ioread32(ab->mem + WINDOW_REG_ADDRESS);
158654e959aSGovind Singh 		ab_pci->register_window = window;
159654e959aSGovind Singh 	}
160654e959aSGovind Singh }
161654e959aSGovind Singh 
162480a7361SKarthikeyan Periyasamy static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci)
163480a7361SKarthikeyan Periyasamy {
164480a7361SKarthikeyan Periyasamy 	u32 umac_window = FIELD_GET(WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET);
165480a7361SKarthikeyan Periyasamy 	u32 ce_window = FIELD_GET(WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE);
166480a7361SKarthikeyan Periyasamy 	u32 window;
167480a7361SKarthikeyan Periyasamy 
168480a7361SKarthikeyan Periyasamy 	window = (umac_window << 12) | (ce_window << 6);
169480a7361SKarthikeyan Periyasamy 
170480a7361SKarthikeyan Periyasamy 	iowrite32(WINDOW_ENABLE_BIT | window, ab_pci->ab->mem + WINDOW_REG_ADDRESS);
171480a7361SKarthikeyan Periyasamy }
172480a7361SKarthikeyan Periyasamy 
173480a7361SKarthikeyan Periyasamy static inline u32 ath11k_pci_get_window_start(struct ath11k_base *ab,
174480a7361SKarthikeyan Periyasamy 					      u32 offset)
175480a7361SKarthikeyan Periyasamy {
176480a7361SKarthikeyan Periyasamy 	u32 window_start;
177480a7361SKarthikeyan Periyasamy 
178480a7361SKarthikeyan Periyasamy 	/* If offset lies within DP register range, use 3rd window */
179480a7361SKarthikeyan Periyasamy 	if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < WINDOW_RANGE_MASK)
180480a7361SKarthikeyan Periyasamy 		window_start = 3 * WINDOW_START;
181480a7361SKarthikeyan Periyasamy 	/* If offset lies within CE register range, use 2nd window */
182480a7361SKarthikeyan Periyasamy 	else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < WINDOW_RANGE_MASK)
183480a7361SKarthikeyan Periyasamy 		window_start = 2 * WINDOW_START;
184480a7361SKarthikeyan Periyasamy 	else
185480a7361SKarthikeyan Periyasamy 		window_start = WINDOW_START;
186480a7361SKarthikeyan Periyasamy 
187480a7361SKarthikeyan Periyasamy 	return window_start;
188480a7361SKarthikeyan Periyasamy }
189480a7361SKarthikeyan Periyasamy 
190f3c603d4SCarl Huang void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
191654e959aSGovind Singh {
192654e959aSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
193480a7361SKarthikeyan Periyasamy 	u32 window_start;
194654e959aSGovind Singh 
195a05bd851SCarl Huang 	/* for offset beyond BAR + 4K - 32, may
196a05bd851SCarl Huang 	 * need to wakeup MHI to access.
197a05bd851SCarl Huang 	 */
198081e2d64SSeevalamuthu Mariappan 	if (ab->hw_params.wakeup_mhi &&
199081e2d64SSeevalamuthu Mariappan 	    test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
200a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
201a05bd851SCarl Huang 		mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
202a05bd851SCarl Huang 
203654e959aSGovind Singh 	if (offset < WINDOW_START) {
204654e959aSGovind Singh 		iowrite32(value, ab->mem  + offset);
205654e959aSGovind Singh 	} else {
206480a7361SKarthikeyan Periyasamy 		if (ab->bus_params.static_window_map)
207480a7361SKarthikeyan Periyasamy 			window_start = ath11k_pci_get_window_start(ab, offset);
208480a7361SKarthikeyan Periyasamy 		else
209480a7361SKarthikeyan Periyasamy 			window_start = WINDOW_START;
210480a7361SKarthikeyan Periyasamy 
211480a7361SKarthikeyan Periyasamy 		if (window_start == WINDOW_START) {
212654e959aSGovind Singh 			spin_lock_bh(&ab_pci->window_lock);
213654e959aSGovind Singh 			ath11k_pci_select_window(ab_pci, offset);
214480a7361SKarthikeyan Periyasamy 			iowrite32(value, ab->mem + window_start +
215480a7361SKarthikeyan Periyasamy 				  (offset & WINDOW_RANGE_MASK));
216654e959aSGovind Singh 			spin_unlock_bh(&ab_pci->window_lock);
217480a7361SKarthikeyan Periyasamy 		} else {
218480a7361SKarthikeyan Periyasamy 			iowrite32(value, ab->mem + window_start +
219480a7361SKarthikeyan Periyasamy 				  (offset & WINDOW_RANGE_MASK));
220480a7361SKarthikeyan Periyasamy 		}
221654e959aSGovind Singh 	}
222a05bd851SCarl Huang 
223081e2d64SSeevalamuthu Mariappan 	if (ab->hw_params.wakeup_mhi &&
224081e2d64SSeevalamuthu Mariappan 	    test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
225a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
226a05bd851SCarl Huang 		mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
227654e959aSGovind Singh }
228654e959aSGovind Singh 
229f3c603d4SCarl Huang u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset)
230654e959aSGovind Singh {
231654e959aSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
232480a7361SKarthikeyan Periyasamy 	u32 val, window_start;
233654e959aSGovind Singh 
234a05bd851SCarl Huang 	/* for offset beyond BAR + 4K - 32, may
235a05bd851SCarl Huang 	 * need to wakeup MHI to access.
236a05bd851SCarl Huang 	 */
237081e2d64SSeevalamuthu Mariappan 	if (ab->hw_params.wakeup_mhi &&
238081e2d64SSeevalamuthu Mariappan 	    test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
239a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
240a05bd851SCarl Huang 		mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
241a05bd851SCarl Huang 
242654e959aSGovind Singh 	if (offset < WINDOW_START) {
243654e959aSGovind Singh 		val = ioread32(ab->mem + offset);
244654e959aSGovind Singh 	} else {
245480a7361SKarthikeyan Periyasamy 		if (ab->bus_params.static_window_map)
246480a7361SKarthikeyan Periyasamy 			window_start = ath11k_pci_get_window_start(ab, offset);
247480a7361SKarthikeyan Periyasamy 		else
248480a7361SKarthikeyan Periyasamy 			window_start = WINDOW_START;
249480a7361SKarthikeyan Periyasamy 
250480a7361SKarthikeyan Periyasamy 		if (window_start == WINDOW_START) {
251654e959aSGovind Singh 			spin_lock_bh(&ab_pci->window_lock);
252654e959aSGovind Singh 			ath11k_pci_select_window(ab_pci, offset);
253480a7361SKarthikeyan Periyasamy 			val = ioread32(ab->mem + window_start +
254480a7361SKarthikeyan Periyasamy 				       (offset & WINDOW_RANGE_MASK));
255654e959aSGovind Singh 			spin_unlock_bh(&ab_pci->window_lock);
256480a7361SKarthikeyan Periyasamy 		} else {
257480a7361SKarthikeyan Periyasamy 			val = ioread32(ab->mem + window_start +
258480a7361SKarthikeyan Periyasamy 				       (offset & WINDOW_RANGE_MASK));
259480a7361SKarthikeyan Periyasamy 		}
260654e959aSGovind Singh 	}
261654e959aSGovind Singh 
262081e2d64SSeevalamuthu Mariappan 	if (ab->hw_params.wakeup_mhi &&
263081e2d64SSeevalamuthu Mariappan 	    test_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags) &&
264a05bd851SCarl Huang 	    offset >= ACCESS_ALWAYS_OFF)
265a05bd851SCarl Huang 		mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
266a05bd851SCarl Huang 
267654e959aSGovind Singh 	return val;
268654e959aSGovind Singh }
269654e959aSGovind Singh 
270f3c603d4SCarl Huang static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
271f3c603d4SCarl Huang {
272f3c603d4SCarl Huang 	u32 val, delay;
273f3c603d4SCarl Huang 
274f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
275f3c603d4SCarl Huang 
276f3c603d4SCarl Huang 	val |= PCIE_SOC_GLOBAL_RESET_V;
277f3c603d4SCarl Huang 
278f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
279f3c603d4SCarl Huang 
280f3c603d4SCarl Huang 	/* TODO: exact time to sleep is uncertain */
281f3c603d4SCarl Huang 	delay = 10;
282f3c603d4SCarl Huang 	mdelay(delay);
283f3c603d4SCarl Huang 
284f3c603d4SCarl Huang 	/* Need to toggle V bit back otherwise stuck in reset status */
285f3c603d4SCarl Huang 	val &= ~PCIE_SOC_GLOBAL_RESET_V;
286f3c603d4SCarl Huang 
287f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
288f3c603d4SCarl Huang 
289f3c603d4SCarl Huang 	mdelay(delay);
290f3c603d4SCarl Huang 
291f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
292f3c603d4SCarl Huang 	if (val == 0xffffffff)
293f3c603d4SCarl Huang 		ath11k_warn(ab, "link down error during global reset\n");
294f3c603d4SCarl Huang }
295f3c603d4SCarl Huang 
296f3c603d4SCarl Huang static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
297f3c603d4SCarl Huang {
298f3c603d4SCarl Huang 	u32 val;
299f3c603d4SCarl Huang 
300f3c603d4SCarl Huang 	/* read cookie */
301f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, PCIE_Q6_COOKIE_ADDR);
302f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "cookie:0x%x\n", val);
303f3c603d4SCarl Huang 
304f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
305f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
306f3c603d4SCarl Huang 
307f3c603d4SCarl Huang 	/* TODO: exact time to sleep is uncertain */
308f3c603d4SCarl Huang 	mdelay(10);
309f3c603d4SCarl Huang 
310f3c603d4SCarl Huang 	/* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from
311f3c603d4SCarl Huang 	 * continuing warm path and entering dead loop.
312f3c603d4SCarl Huang 	 */
313f3c603d4SCarl Huang 	ath11k_pci_write32(ab, WLAON_WARM_SW_ENTRY, 0);
314f3c603d4SCarl Huang 	mdelay(10);
315f3c603d4SCarl Huang 
316f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
317f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
318f3c603d4SCarl Huang 
319f3c603d4SCarl Huang 	/* A read clear register. clear the register to prevent
320f3c603d4SCarl Huang 	 * Q6 from entering wrong code path.
321f3c603d4SCarl Huang 	 */
322f3c603d4SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_SOC_RESET_CAUSE_REG);
323f3c603d4SCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val);
324f3c603d4SCarl Huang }
325f3c603d4SCarl Huang 
32606999407SCarl Huang static int ath11k_pci_set_link_reg(struct ath11k_base *ab,
32706999407SCarl Huang 				   u32 offset, u32 value, u32 mask)
32806999407SCarl Huang {
32906999407SCarl Huang 	u32 v;
33006999407SCarl Huang 	int i;
33106999407SCarl Huang 
33206999407SCarl Huang 	v = ath11k_pci_read32(ab, offset);
33306999407SCarl Huang 	if ((v & mask) == value)
33406999407SCarl Huang 		return 0;
33506999407SCarl Huang 
33606999407SCarl Huang 	for (i = 0; i < 10; i++) {
33706999407SCarl Huang 		ath11k_pci_write32(ab, offset, (v & ~mask) | value);
33806999407SCarl Huang 
33906999407SCarl Huang 		v = ath11k_pci_read32(ab, offset);
34006999407SCarl Huang 		if ((v & mask) == value)
34106999407SCarl Huang 			return 0;
34206999407SCarl Huang 
34306999407SCarl Huang 		mdelay(2);
34406999407SCarl Huang 	}
34506999407SCarl Huang 
34606999407SCarl Huang 	ath11k_warn(ab, "failed to set pcie link register 0x%08x: 0x%08x != 0x%08x\n",
34706999407SCarl Huang 		    offset, v & mask, value);
34806999407SCarl Huang 
34906999407SCarl Huang 	return -ETIMEDOUT;
35006999407SCarl Huang }
35106999407SCarl Huang 
35206999407SCarl Huang static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
35306999407SCarl Huang {
35406999407SCarl Huang 	int ret;
35506999407SCarl Huang 
35606999407SCarl Huang 	ret = ath11k_pci_set_link_reg(ab,
3576fe6f68fSKarthikeyan Periyasamy 				      PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab),
35806999407SCarl Huang 				      PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL,
35906999407SCarl Huang 				      PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK);
36030d08503SDan Carpenter 	if (ret) {
36106999407SCarl Huang 		ath11k_warn(ab, "failed to set sysclk: %d\n", ret);
36206999407SCarl Huang 		return ret;
36306999407SCarl Huang 	}
36406999407SCarl Huang 
36506999407SCarl Huang 	ret = ath11k_pci_set_link_reg(ab,
3666fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab),
3676fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG1_VAL,
3686fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG_MSK);
36930d08503SDan Carpenter 	if (ret) {
37006999407SCarl Huang 		ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret);
37106999407SCarl Huang 		return ret;
37206999407SCarl Huang 	}
37306999407SCarl Huang 
37406999407SCarl Huang 	ret = ath11k_pci_set_link_reg(ab,
3756fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab),
3766fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG2_VAL,
3776fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG_MSK);
37830d08503SDan Carpenter 	if (ret) {
37906999407SCarl Huang 		ath11k_warn(ab, "failed to set dtct config2: %d\n", ret);
38006999407SCarl Huang 		return ret;
38106999407SCarl Huang 	}
38206999407SCarl Huang 
38306999407SCarl Huang 	ret = ath11k_pci_set_link_reg(ab,
3846fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab),
3856fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG4_VAL,
3866fe6f68fSKarthikeyan Periyasamy 				      PCIE_PCS_OSC_DTCT_CONFIG_MSK);
38730d08503SDan Carpenter 	if (ret) {
38806999407SCarl Huang 		ath11k_warn(ab, "failed to set dtct config4: %d\n", ret);
38906999407SCarl Huang 		return ret;
39006999407SCarl Huang 	}
39106999407SCarl Huang 
39206999407SCarl Huang 	return 0;
39306999407SCarl Huang }
39406999407SCarl Huang 
395babb0cedSCarl Huang static void ath11k_pci_enable_ltssm(struct ath11k_base *ab)
396babb0cedSCarl Huang {
397babb0cedSCarl Huang 	u32 val;
398babb0cedSCarl Huang 	int i;
399babb0cedSCarl Huang 
400babb0cedSCarl Huang 	val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM);
401babb0cedSCarl Huang 
402babb0cedSCarl Huang 	/* PCIE link seems very unstable after the Hot Reset*/
403babb0cedSCarl Huang 	for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) {
404babb0cedSCarl Huang 		if (val == 0xffffffff)
405babb0cedSCarl Huang 			mdelay(5);
406babb0cedSCarl Huang 
407babb0cedSCarl Huang 		ath11k_pci_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE);
408babb0cedSCarl Huang 		val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM);
409babb0cedSCarl Huang 	}
410babb0cedSCarl Huang 
411babb0cedSCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "pci ltssm 0x%x\n", val);
412babb0cedSCarl Huang 
413babb0cedSCarl Huang 	val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
414562934adSKalle Valo 	val |= GCC_GCC_PCIE_HOT_RST_VAL;
415babb0cedSCarl Huang 	ath11k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST, val);
416babb0cedSCarl Huang 	val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
417babb0cedSCarl Huang 
418babb0cedSCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "pci pcie_hot_rst 0x%x\n", val);
419babb0cedSCarl Huang 
420babb0cedSCarl Huang 	mdelay(5);
421babb0cedSCarl Huang }
422babb0cedSCarl Huang 
423babb0cedSCarl Huang static void ath11k_pci_clear_all_intrs(struct ath11k_base *ab)
424babb0cedSCarl Huang {
425babb0cedSCarl Huang 	/* This is a WAR for PCIE Hotreset.
426babb0cedSCarl Huang 	 * When target receive Hotreset, but will set the interrupt.
427babb0cedSCarl Huang 	 * So when download SBL again, SBL will open Interrupt and
428babb0cedSCarl Huang 	 * receive it, and crash immediately.
429babb0cedSCarl Huang 	 */
430babb0cedSCarl Huang 	ath11k_pci_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL);
431babb0cedSCarl Huang }
432babb0cedSCarl Huang 
4330ccdf439SCarl Huang static void ath11k_pci_set_wlaon_pwr_ctrl(struct ath11k_base *ab)
4340ccdf439SCarl Huang {
4350ccdf439SCarl Huang 	u32 val;
4360ccdf439SCarl Huang 
4370ccdf439SCarl Huang 	val = ath11k_pci_read32(ab, WLAON_QFPROM_PWR_CTRL_REG);
4380ccdf439SCarl Huang 	val &= ~QFPROM_PWR_CTRL_VDD4BLOW_MASK;
4390ccdf439SCarl Huang 	ath11k_pci_write32(ab, WLAON_QFPROM_PWR_CTRL_REG, val);
4400ccdf439SCarl Huang }
4410ccdf439SCarl Huang 
442f3c603d4SCarl Huang static void ath11k_pci_force_wake(struct ath11k_base *ab)
443f3c603d4SCarl Huang {
444f3c603d4SCarl Huang 	ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1);
445f3c603d4SCarl Huang 	mdelay(5);
446f3c603d4SCarl Huang }
447f3c603d4SCarl Huang 
448babb0cedSCarl Huang static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on)
449f3c603d4SCarl Huang {
4508a0b899fSBaochen Qiang 	mdelay(100);
4518a0b899fSBaochen Qiang 
452babb0cedSCarl Huang 	if (power_on) {
453babb0cedSCarl Huang 		ath11k_pci_enable_ltssm(ab);
454babb0cedSCarl Huang 		ath11k_pci_clear_all_intrs(ab);
4550ccdf439SCarl Huang 		ath11k_pci_set_wlaon_pwr_ctrl(ab);
4565088df05SBaochen Qiang 		if (ab->hw_params.fix_l1ss)
45706999407SCarl Huang 			ath11k_pci_fix_l1ss(ab);
458babb0cedSCarl Huang 	}
459babb0cedSCarl Huang 
460f3c603d4SCarl Huang 	ath11k_mhi_clear_vector(ab);
4618a0b899fSBaochen Qiang 	ath11k_pci_clear_dbg_registers(ab);
462f3c603d4SCarl Huang 	ath11k_pci_soc_global_reset(ab);
463f3c603d4SCarl Huang 	ath11k_mhi_set_mhictrl_reset(ab);
464f3c603d4SCarl Huang }
465f3c603d4SCarl Huang 
4661399fb87SGovind Singh int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector)
4671399fb87SGovind Singh {
4681399fb87SGovind Singh 	struct pci_dev *pci_dev = to_pci_dev(dev);
4691399fb87SGovind Singh 
4701399fb87SGovind Singh 	return pci_irq_vector(pci_dev, vector);
4711399fb87SGovind Singh }
4721399fb87SGovind Singh 
473c4eacabeSGovind Singh static void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
474c4eacabeSGovind Singh 				       u32 *msi_addr_hi)
475c4eacabeSGovind Singh {
476e8e55d89SAnilkumar Kolli 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
477c4eacabeSGovind Singh 	struct pci_dev *pci_dev = to_pci_dev(ab->dev);
478c4eacabeSGovind Singh 
479c4eacabeSGovind Singh 	pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO,
480c4eacabeSGovind Singh 			      msi_addr_lo);
481c4eacabeSGovind Singh 
482e8e55d89SAnilkumar Kolli 	if (test_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags)) {
483c4eacabeSGovind Singh 		pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI,
484c4eacabeSGovind Singh 				      msi_addr_hi);
485e8e55d89SAnilkumar Kolli 	} else {
486e8e55d89SAnilkumar Kolli 		*msi_addr_hi = 0;
487e8e55d89SAnilkumar Kolli 	}
488c4eacabeSGovind Singh }
489c4eacabeSGovind Singh 
4901399fb87SGovind Singh int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name,
4911399fb87SGovind Singh 				       int *num_vectors, u32 *user_base_data,
4921399fb87SGovind Singh 				       u32 *base_vector)
4931399fb87SGovind Singh {
4941399fb87SGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
4957a3aed0cSAnilkumar Kolli 	const struct ath11k_msi_config *msi_config = ab_pci->msi_config;
4961399fb87SGovind Singh 	int idx;
4971399fb87SGovind Singh 
4987a3aed0cSAnilkumar Kolli 	for (idx = 0; idx < msi_config->total_users; idx++) {
4997a3aed0cSAnilkumar Kolli 		if (strcmp(user_name, msi_config->users[idx].name) == 0) {
5007a3aed0cSAnilkumar Kolli 			*num_vectors = msi_config->users[idx].num_vectors;
5017a3aed0cSAnilkumar Kolli 			*base_vector =  msi_config->users[idx].base_vector;
502c41a6700SCarl Huang 			*user_base_data = *base_vector + ab_pci->msi_ep_base_data;
5031399fb87SGovind Singh 
504c41a6700SCarl Huang 			ath11k_dbg(ab, ATH11K_DBG_PCI,
505c41a6700SCarl Huang 				   "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
5061399fb87SGovind Singh 				   user_name, *num_vectors, *user_base_data,
5071399fb87SGovind Singh 				   *base_vector);
5081399fb87SGovind Singh 
5091399fb87SGovind Singh 			return 0;
5101399fb87SGovind Singh 		}
5111399fb87SGovind Singh 	}
5121399fb87SGovind Singh 
5131399fb87SGovind Singh 	ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);
5141399fb87SGovind Singh 
5151399fb87SGovind Singh 	return -EINVAL;
5161399fb87SGovind Singh }
5171399fb87SGovind Singh 
5186289ac2bSKarthikeyan Periyasamy static void ath11k_pci_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
5196289ac2bSKarthikeyan Periyasamy 				      u32 *msi_idx)
5206289ac2bSKarthikeyan Periyasamy {
5216289ac2bSKarthikeyan Periyasamy 	u32 i, msi_data_idx;
5226289ac2bSKarthikeyan Periyasamy 
5236289ac2bSKarthikeyan Periyasamy 	for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
5246289ac2bSKarthikeyan Periyasamy 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
5256289ac2bSKarthikeyan Periyasamy 			continue;
5266289ac2bSKarthikeyan Periyasamy 
5276289ac2bSKarthikeyan Periyasamy 		if (ce_id == i)
5286289ac2bSKarthikeyan Periyasamy 			break;
5296289ac2bSKarthikeyan Periyasamy 
5306289ac2bSKarthikeyan Periyasamy 		msi_data_idx++;
5316289ac2bSKarthikeyan Periyasamy 	}
5326289ac2bSKarthikeyan Periyasamy 	*msi_idx = msi_data_idx;
5336289ac2bSKarthikeyan Periyasamy }
5346289ac2bSKarthikeyan Periyasamy 
535c4eacabeSGovind Singh static int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
536c4eacabeSGovind Singh 					  int *num_vectors, u32 *user_base_data,
537c4eacabeSGovind Singh 					  u32 *base_vector)
538c4eacabeSGovind Singh {
539c4eacabeSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
540c4eacabeSGovind Singh 
541c4eacabeSGovind Singh 	return ath11k_pci_get_user_msi_assignment(ab_pci, user_name,
542c4eacabeSGovind Singh 						  num_vectors, user_base_data,
543c4eacabeSGovind Singh 						  base_vector);
544c4eacabeSGovind Singh }
545c4eacabeSGovind Singh 
546d4ecb90bSCarl Huang static void ath11k_pci_free_ext_irq(struct ath11k_base *ab)
547d4ecb90bSCarl Huang {
548d4ecb90bSCarl Huang 	int i, j;
549d4ecb90bSCarl Huang 
550d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
551d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
552d4ecb90bSCarl Huang 
553d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++)
554d4ecb90bSCarl Huang 			free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
555d4ecb90bSCarl Huang 
556d4ecb90bSCarl Huang 		netif_napi_del(&irq_grp->napi);
557d4ecb90bSCarl Huang 	}
558d4ecb90bSCarl Huang }
559d4ecb90bSCarl Huang 
5607f4beda2SGovind Singh static void ath11k_pci_free_irq(struct ath11k_base *ab)
5617f4beda2SGovind Singh {
5627f4beda2SGovind Singh 	int i, irq_idx;
5637f4beda2SGovind Singh 
564d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
565e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
5667f4beda2SGovind Singh 			continue;
5677f4beda2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
5687f4beda2SGovind Singh 		free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
5697f4beda2SGovind Singh 	}
570d4ecb90bSCarl Huang 
571d4ecb90bSCarl Huang 	ath11k_pci_free_ext_irq(ab);
5727f4beda2SGovind Singh }
5737f4beda2SGovind Singh 
5742c3960c2SGovind Singh static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
5752c3960c2SGovind Singh {
576c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
5772c3960c2SGovind Singh 	u32 irq_idx;
5782c3960c2SGovind Singh 
579c41a6700SCarl Huang 	/* In case of one MSI vector, we handle irq enable/disable in a
580c41a6700SCarl Huang 	 * uniform way since we only have one irq
581c41a6700SCarl Huang 	 */
582c41a6700SCarl Huang 	if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
583c41a6700SCarl Huang 		return;
584c41a6700SCarl Huang 
5852c3960c2SGovind Singh 	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
5862c3960c2SGovind Singh 	enable_irq(ab->irq_num[irq_idx]);
5872c3960c2SGovind Singh }
5882c3960c2SGovind Singh 
5897f4beda2SGovind Singh static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
5907f4beda2SGovind Singh {
591c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
5927f4beda2SGovind Singh 	u32 irq_idx;
5937f4beda2SGovind Singh 
594c41a6700SCarl Huang 	/* In case of one MSI vector, we handle irq enable/disable in a
595c41a6700SCarl Huang 	 * uniform way since we only have one irq
596c41a6700SCarl Huang 	 */
597c41a6700SCarl Huang 	if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
598c41a6700SCarl Huang 		return;
599c41a6700SCarl Huang 
6007f4beda2SGovind Singh 	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
6017f4beda2SGovind Singh 	disable_irq_nosync(ab->irq_num[irq_idx]);
6027f4beda2SGovind Singh }
6037f4beda2SGovind Singh 
6042c3960c2SGovind Singh static void ath11k_pci_ce_irqs_disable(struct ath11k_base *ab)
6052c3960c2SGovind Singh {
6062c3960c2SGovind Singh 	int i;
6072c3960c2SGovind Singh 
60801279bcdSCarl Huang 	clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
60901279bcdSCarl Huang 
610d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
611e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
6122c3960c2SGovind Singh 			continue;
6132c3960c2SGovind Singh 		ath11k_pci_ce_irq_disable(ab, i);
6142c3960c2SGovind Singh 	}
6152c3960c2SGovind Singh }
6162c3960c2SGovind Singh 
6172c3960c2SGovind Singh static void ath11k_pci_sync_ce_irqs(struct ath11k_base *ab)
6182c3960c2SGovind Singh {
6192c3960c2SGovind Singh 	int i;
6202c3960c2SGovind Singh 	int irq_idx;
6212c3960c2SGovind Singh 
622d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
623e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
6242c3960c2SGovind Singh 			continue;
6252c3960c2SGovind Singh 
6262c3960c2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
6272c3960c2SGovind Singh 		synchronize_irq(ab->irq_num[irq_idx]);
6282c3960c2SGovind Singh 	}
6292c3960c2SGovind Singh }
6302c3960c2SGovind Singh 
6310f01dcb8SAllen Pais static void ath11k_pci_ce_tasklet(struct tasklet_struct *t)
6322c3960c2SGovind Singh {
6330f01dcb8SAllen Pais 	struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);
634ac6e7348SCarl Huang 	int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
6352c3960c2SGovind Singh 
6362c3960c2SGovind Singh 	ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
6372c3960c2SGovind Singh 
638ac6e7348SCarl Huang 	enable_irq(ce_pipe->ab->irq_num[irq_idx]);
6392c3960c2SGovind Singh }
6402c3960c2SGovind Singh 
6417f4beda2SGovind Singh static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
6427f4beda2SGovind Singh {
6437f4beda2SGovind Singh 	struct ath11k_ce_pipe *ce_pipe = arg;
64401279bcdSCarl Huang 	struct ath11k_base *ab = ce_pipe->ab;
645ac6e7348SCarl Huang 	int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
64601279bcdSCarl Huang 
64701279bcdSCarl Huang 	if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))
64801279bcdSCarl Huang 		return IRQ_HANDLED;
6497f4beda2SGovind Singh 
6507dc67af0SKarthikeyan Periyasamy 	/* last interrupt received for this CE */
6517dc67af0SKarthikeyan Periyasamy 	ce_pipe->timestamp = jiffies;
6527dc67af0SKarthikeyan Periyasamy 
653ac6e7348SCarl Huang 	disable_irq_nosync(ab->irq_num[irq_idx]);
654ac6e7348SCarl Huang 
6552c3960c2SGovind Singh 	tasklet_schedule(&ce_pipe->intr_tq);
6567f4beda2SGovind Singh 
6577f4beda2SGovind Singh 	return IRQ_HANDLED;
6587f4beda2SGovind Singh }
6597f4beda2SGovind Singh 
660d4ecb90bSCarl Huang static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
661d4ecb90bSCarl Huang {
662c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab);
663d4ecb90bSCarl Huang 	int i;
664d4ecb90bSCarl Huang 
665c41a6700SCarl Huang 	/* In case of one MSI vector, we handle irq enable/disable
666c41a6700SCarl Huang 	 * in a uniform way since we only have one irq
667c41a6700SCarl Huang 	 */
668c41a6700SCarl Huang 	if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
669c41a6700SCarl Huang 		return;
670c41a6700SCarl Huang 
671d4ecb90bSCarl Huang 	for (i = 0; i < irq_grp->num_irq; i++)
672d4ecb90bSCarl Huang 		disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
673d4ecb90bSCarl Huang }
674d4ecb90bSCarl Huang 
675d4ecb90bSCarl Huang static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc)
676d4ecb90bSCarl Huang {
677d4ecb90bSCarl Huang 	int i;
678d4ecb90bSCarl Huang 
67901279bcdSCarl Huang 	clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags);
68001279bcdSCarl Huang 
681d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
682d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i];
683d4ecb90bSCarl Huang 
684d4ecb90bSCarl Huang 		ath11k_pci_ext_grp_disable(irq_grp);
685d4ecb90bSCarl Huang 
686d943fdadSBen Greear 		if (irq_grp->napi_enabled) {
687d4ecb90bSCarl Huang 			napi_synchronize(&irq_grp->napi);
688d4ecb90bSCarl Huang 			napi_disable(&irq_grp->napi);
689d943fdadSBen Greear 			irq_grp->napi_enabled = false;
690d943fdadSBen Greear 		}
691d4ecb90bSCarl Huang 	}
692d4ecb90bSCarl Huang }
693d4ecb90bSCarl Huang 
694d4ecb90bSCarl Huang static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
695d4ecb90bSCarl Huang {
696c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(irq_grp->ab);
697d4ecb90bSCarl Huang 	int i;
698d4ecb90bSCarl Huang 
699c41a6700SCarl Huang 	/* In case of one MSI vector, we handle irq enable/disable in a
700c41a6700SCarl Huang 	 * uniform way since we only have one irq
701c41a6700SCarl Huang 	 */
702c41a6700SCarl Huang 	if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
703c41a6700SCarl Huang 		return;
704c41a6700SCarl Huang 
705d4ecb90bSCarl Huang 	for (i = 0; i < irq_grp->num_irq; i++)
706d4ecb90bSCarl Huang 		enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
707d4ecb90bSCarl Huang }
708d4ecb90bSCarl Huang 
709d4ecb90bSCarl Huang static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab)
710d4ecb90bSCarl Huang {
711d4ecb90bSCarl Huang 	int i;
712d4ecb90bSCarl Huang 
71301279bcdSCarl Huang 	set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
71401279bcdSCarl Huang 
715d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
716d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
717d4ecb90bSCarl Huang 
718d943fdadSBen Greear 		if (!irq_grp->napi_enabled) {
719d4ecb90bSCarl Huang 			napi_enable(&irq_grp->napi);
720d943fdadSBen Greear 			irq_grp->napi_enabled = true;
721d943fdadSBen Greear 		}
722d4ecb90bSCarl Huang 		ath11k_pci_ext_grp_enable(irq_grp);
723d4ecb90bSCarl Huang 	}
724d4ecb90bSCarl Huang }
725d4ecb90bSCarl Huang 
726d4ecb90bSCarl Huang static void ath11k_pci_sync_ext_irqs(struct ath11k_base *ab)
727d4ecb90bSCarl Huang {
728d4ecb90bSCarl Huang 	int i, j, irq_idx;
729d4ecb90bSCarl Huang 
730d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
731d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
732d4ecb90bSCarl Huang 
733d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++) {
734d4ecb90bSCarl Huang 			irq_idx = irq_grp->irqs[j];
735d4ecb90bSCarl Huang 			synchronize_irq(ab->irq_num[irq_idx]);
736d4ecb90bSCarl Huang 		}
737d4ecb90bSCarl Huang 	}
738d4ecb90bSCarl Huang }
739d4ecb90bSCarl Huang 
740d4ecb90bSCarl Huang static void ath11k_pci_ext_irq_disable(struct ath11k_base *ab)
741d4ecb90bSCarl Huang {
742d4ecb90bSCarl Huang 	__ath11k_pci_ext_irq_disable(ab);
743d4ecb90bSCarl Huang 	ath11k_pci_sync_ext_irqs(ab);
744d4ecb90bSCarl Huang }
745d4ecb90bSCarl Huang 
746d4ecb90bSCarl Huang static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget)
747d4ecb90bSCarl Huang {
748d4ecb90bSCarl Huang 	struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
749d4ecb90bSCarl Huang 						struct ath11k_ext_irq_grp,
750d4ecb90bSCarl Huang 						napi);
751d4ecb90bSCarl Huang 	struct ath11k_base *ab = irq_grp->ab;
752d4ecb90bSCarl Huang 	int work_done;
753ac6e7348SCarl Huang 	int i;
754d4ecb90bSCarl Huang 
755d4ecb90bSCarl Huang 	work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
756d4ecb90bSCarl Huang 	if (work_done < budget) {
757d4ecb90bSCarl Huang 		napi_complete_done(napi, work_done);
758ac6e7348SCarl Huang 		for (i = 0; i < irq_grp->num_irq; i++)
759ac6e7348SCarl Huang 			enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
760d4ecb90bSCarl Huang 	}
761d4ecb90bSCarl Huang 
762d4ecb90bSCarl Huang 	if (work_done > budget)
763d4ecb90bSCarl Huang 		work_done = budget;
764d4ecb90bSCarl Huang 
765d4ecb90bSCarl Huang 	return work_done;
766d4ecb90bSCarl Huang }
767d4ecb90bSCarl Huang 
768d4ecb90bSCarl Huang static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg)
769d4ecb90bSCarl Huang {
770d4ecb90bSCarl Huang 	struct ath11k_ext_irq_grp *irq_grp = arg;
77101279bcdSCarl Huang 	struct ath11k_base *ab = irq_grp->ab;
772ac6e7348SCarl Huang 	int i;
77301279bcdSCarl Huang 
77401279bcdSCarl Huang 	if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))
77501279bcdSCarl Huang 		return IRQ_HANDLED;
776d4ecb90bSCarl Huang 
777d4ecb90bSCarl Huang 	ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq);
778d4ecb90bSCarl Huang 
7797dc67af0SKarthikeyan Periyasamy 	/* last interrupt received for this group */
7807dc67af0SKarthikeyan Periyasamy 	irq_grp->timestamp = jiffies;
7817dc67af0SKarthikeyan Periyasamy 
782ac6e7348SCarl Huang 	for (i = 0; i < irq_grp->num_irq; i++)
783ac6e7348SCarl Huang 		disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
784d4ecb90bSCarl Huang 
785d4ecb90bSCarl Huang 	napi_schedule(&irq_grp->napi);
786d4ecb90bSCarl Huang 
787d4ecb90bSCarl Huang 	return IRQ_HANDLED;
788d4ecb90bSCarl Huang }
789d4ecb90bSCarl Huang 
790d4ecb90bSCarl Huang static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
791d4ecb90bSCarl Huang {
792c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
793d4ecb90bSCarl Huang 	int i, j, ret, num_vectors = 0;
7944ab4693fSCarl Huang 	u32 user_base_data = 0, base_vector = 0;
795d4ecb90bSCarl Huang 
796b2c09458SColin Ian King 	ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP",
797b2c09458SColin Ian King 						 &num_vectors,
798b2c09458SColin Ian King 						 &user_base_data,
799d4ecb90bSCarl Huang 						 &base_vector);
800b2c09458SColin Ian King 	if (ret < 0)
801b2c09458SColin Ian King 		return ret;
802d4ecb90bSCarl Huang 
803d4ecb90bSCarl Huang 	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
804d4ecb90bSCarl Huang 		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
805d4ecb90bSCarl Huang 		u32 num_irq = 0;
806d4ecb90bSCarl Huang 
807d4ecb90bSCarl Huang 		irq_grp->ab = ab;
808d4ecb90bSCarl Huang 		irq_grp->grp_id = i;
809d4ecb90bSCarl Huang 		init_dummy_netdev(&irq_grp->napi_ndev);
810d4ecb90bSCarl Huang 		netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
811d4ecb90bSCarl Huang 			       ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
812d4ecb90bSCarl Huang 
813d4ecb90bSCarl Huang 		if (ab->hw_params.ring_mask->tx[i] ||
814d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx[i] ||
815d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_err[i] ||
816d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_wbm_rel[i] ||
817d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->reo_status[i] ||
818d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rxdma2host[i] ||
819d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->host2rxdma[i] ||
820d4ecb90bSCarl Huang 		    ab->hw_params.ring_mask->rx_mon_status[i]) {
821d4ecb90bSCarl Huang 			num_irq = 1;
822d4ecb90bSCarl Huang 		}
823d4ecb90bSCarl Huang 
824d4ecb90bSCarl Huang 		irq_grp->num_irq = num_irq;
8254ab4693fSCarl Huang 		irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;
826d4ecb90bSCarl Huang 
827d4ecb90bSCarl Huang 		for (j = 0; j < irq_grp->num_irq; j++) {
828d4ecb90bSCarl Huang 			int irq_idx = irq_grp->irqs[j];
829d4ecb90bSCarl Huang 			int vector = (i % num_vectors) + base_vector;
830d4ecb90bSCarl Huang 			int irq = ath11k_pci_get_msi_irq(ab->dev, vector);
831d4ecb90bSCarl Huang 
832d4ecb90bSCarl Huang 			ab->irq_num[irq_idx] = irq;
833d4ecb90bSCarl Huang 
834d4ecb90bSCarl Huang 			ath11k_dbg(ab, ATH11K_DBG_PCI,
835d4ecb90bSCarl Huang 				   "irq:%d group:%d\n", irq, i);
8367dc67af0SKarthikeyan Periyasamy 
8377dc67af0SKarthikeyan Periyasamy 			irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
838d4ecb90bSCarl Huang 			ret = request_irq(irq, ath11k_pci_ext_interrupt_handler,
839c41a6700SCarl Huang 					  ab_pci->irq_flags,
840d4ecb90bSCarl Huang 					  "DP_EXT_IRQ", irq_grp);
841d4ecb90bSCarl Huang 			if (ret) {
842d4ecb90bSCarl Huang 				ath11k_err(ab, "failed request irq %d: %d\n",
843d4ecb90bSCarl Huang 					   vector, ret);
844d4ecb90bSCarl Huang 				return ret;
845d4ecb90bSCarl Huang 			}
846d4ecb90bSCarl Huang 		}
847c41a6700SCarl Huang 		ath11k_pci_ext_grp_disable(irq_grp);
848d4ecb90bSCarl Huang 	}
849d4ecb90bSCarl Huang 
850d4ecb90bSCarl Huang 	return 0;
851d4ecb90bSCarl Huang }
852d4ecb90bSCarl Huang 
853e94b0749SBaochen Qiang static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci,
854e94b0749SBaochen Qiang 					    const struct cpumask *m)
855e94b0749SBaochen Qiang {
856e94b0749SBaochen Qiang 	if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
857e94b0749SBaochen Qiang 		return 0;
858e94b0749SBaochen Qiang 
859e94b0749SBaochen Qiang 	return irq_set_affinity_hint(ab_pci->pdev->irq, m);
860e94b0749SBaochen Qiang }
861e94b0749SBaochen Qiang 
8627f4beda2SGovind Singh static int ath11k_pci_config_irq(struct ath11k_base *ab)
8637f4beda2SGovind Singh {
864c41a6700SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
8657f4beda2SGovind Singh 	struct ath11k_ce_pipe *ce_pipe;
8667f4beda2SGovind Singh 	u32 msi_data_start;
8676289ac2bSKarthikeyan Periyasamy 	u32 msi_data_count, msi_data_idx;
8687f4beda2SGovind Singh 	u32 msi_irq_start;
8697f4beda2SGovind Singh 	unsigned int msi_data;
8707f4beda2SGovind Singh 	int irq, i, ret, irq_idx;
8717f4beda2SGovind Singh 
8727f4beda2SGovind Singh 	ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab),
8737f4beda2SGovind Singh 						 "CE", &msi_data_count,
8747f4beda2SGovind Singh 						 &msi_data_start, &msi_irq_start);
8757f4beda2SGovind Singh 	if (ret)
8767f4beda2SGovind Singh 		return ret;
8777f4beda2SGovind Singh 
878e94b0749SBaochen Qiang 	ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0));
879e94b0749SBaochen Qiang 	if (ret) {
880e94b0749SBaochen Qiang 		ath11k_err(ab, "failed to set irq affinity %d\n", ret);
881e94b0749SBaochen Qiang 		return ret;
882e94b0749SBaochen Qiang 	}
883e94b0749SBaochen Qiang 
8847f4beda2SGovind Singh 	/* Configure CE irqs */
8856289ac2bSKarthikeyan Periyasamy 	for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
886e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
8877f4beda2SGovind Singh 			continue;
8887f4beda2SGovind Singh 
8896289ac2bSKarthikeyan Periyasamy 		msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;
8906289ac2bSKarthikeyan Periyasamy 		irq = ath11k_pci_get_msi_irq(ab->dev, msi_data);
8916289ac2bSKarthikeyan Periyasamy 		ce_pipe = &ab->ce.ce_pipe[i];
8926289ac2bSKarthikeyan Periyasamy 
8937f4beda2SGovind Singh 		irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
8947f4beda2SGovind Singh 
8950f01dcb8SAllen Pais 		tasklet_setup(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet);
8962c3960c2SGovind Singh 
8977f4beda2SGovind Singh 		ret = request_irq(irq, ath11k_pci_ce_interrupt_handler,
898c41a6700SCarl Huang 				  ab_pci->irq_flags, irq_name[irq_idx],
8997f4beda2SGovind Singh 				  ce_pipe);
9007f4beda2SGovind Singh 		if (ret) {
9017f4beda2SGovind Singh 			ath11k_err(ab, "failed to request irq %d: %d\n",
9027f4beda2SGovind Singh 				   irq_idx, ret);
903e94b0749SBaochen Qiang 			goto err_irq_affinity_cleanup;
9047f4beda2SGovind Singh 		}
9057f4beda2SGovind Singh 
9067f4beda2SGovind Singh 		ab->irq_num[irq_idx] = irq;
9076289ac2bSKarthikeyan Periyasamy 		msi_data_idx++;
908e678fbd4SKarthikeyan Periyasamy 
909e5c860e1SCarl Huang 		ath11k_pci_ce_irq_disable(ab, i);
9107f4beda2SGovind Singh 	}
9117f4beda2SGovind Singh 
912d4ecb90bSCarl Huang 	ret = ath11k_pci_ext_irq_config(ab);
913d4ecb90bSCarl Huang 	if (ret)
914e94b0749SBaochen Qiang 		goto err_irq_affinity_cleanup;
915d4ecb90bSCarl Huang 
9167f4beda2SGovind Singh 	return 0;
917e94b0749SBaochen Qiang 
918e94b0749SBaochen Qiang err_irq_affinity_cleanup:
919e94b0749SBaochen Qiang 	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
920e94b0749SBaochen Qiang 	return ret;
9217f4beda2SGovind Singh }
9227f4beda2SGovind Singh 
9237f4beda2SGovind Singh static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
9247f4beda2SGovind Singh {
9257f4beda2SGovind Singh 	struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
9267f4beda2SGovind Singh 
927967c1d11SAnilkumar Kolli 	cfg->tgt_ce = ab->hw_params.target_ce_config;
928967c1d11SAnilkumar Kolli 	cfg->tgt_ce_len = ab->hw_params.target_ce_count;
9297f4beda2SGovind Singh 
930967c1d11SAnilkumar Kolli 	cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map;
931967c1d11SAnilkumar Kolli 	cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len;
93216001e4bSAnilkumar Kolli 	ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
933e838c14aSCarl Huang 
934e838c14aSCarl Huang 	ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2,
935e838c14aSCarl Huang 				    &cfg->shadow_reg_v2_len);
9367f4beda2SGovind Singh }
9377f4beda2SGovind Singh 
9387f4beda2SGovind Singh static void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab)
9397f4beda2SGovind Singh {
9407f4beda2SGovind Singh 	int i;
9417f4beda2SGovind Singh 
94201279bcdSCarl Huang 	set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
94301279bcdSCarl Huang 
944d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
945e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
9467f4beda2SGovind Singh 			continue;
9477f4beda2SGovind Singh 		ath11k_pci_ce_irq_enable(ab, i);
9487f4beda2SGovind Singh 	}
9497f4beda2SGovind Singh }
9507f4beda2SGovind Singh 
95196527d52SBaochen Qiang static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable)
95296527d52SBaochen Qiang {
95396527d52SBaochen Qiang 	struct pci_dev *dev = ab_pci->pdev;
95496527d52SBaochen Qiang 	u16 control;
95596527d52SBaochen Qiang 
95696527d52SBaochen Qiang 	pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
95796527d52SBaochen Qiang 
95896527d52SBaochen Qiang 	if (enable)
95996527d52SBaochen Qiang 		control |= PCI_MSI_FLAGS_ENABLE;
96096527d52SBaochen Qiang 	else
96196527d52SBaochen Qiang 		control &= ~PCI_MSI_FLAGS_ENABLE;
96296527d52SBaochen Qiang 
96396527d52SBaochen Qiang 	pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
96496527d52SBaochen Qiang }
96596527d52SBaochen Qiang 
96696527d52SBaochen Qiang static void ath11k_pci_msi_enable(struct ath11k_pci *ab_pci)
96796527d52SBaochen Qiang {
96896527d52SBaochen Qiang 	ath11k_pci_msi_config(ab_pci, true);
96996527d52SBaochen Qiang }
97096527d52SBaochen Qiang 
97196527d52SBaochen Qiang static void ath11k_pci_msi_disable(struct ath11k_pci *ab_pci)
97296527d52SBaochen Qiang {
97396527d52SBaochen Qiang 	ath11k_pci_msi_config(ab_pci, false);
97496527d52SBaochen Qiang }
97596527d52SBaochen Qiang 
97696527d52SBaochen Qiang static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci)
9775697a564SGovind Singh {
9785697a564SGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
9797a3aed0cSAnilkumar Kolli 	const struct ath11k_msi_config *msi_config = ab_pci->msi_config;
9805697a564SGovind Singh 	struct msi_desc *msi_desc;
9815697a564SGovind Singh 	int num_vectors;
9825697a564SGovind Singh 	int ret;
9835697a564SGovind Singh 
9845697a564SGovind Singh 	num_vectors = pci_alloc_irq_vectors(ab_pci->pdev,
9857a3aed0cSAnilkumar Kolli 					    msi_config->total_vectors,
9867a3aed0cSAnilkumar Kolli 					    msi_config->total_vectors,
9875697a564SGovind Singh 					    PCI_IRQ_MSI);
988ac6e7348SCarl Huang 	if (num_vectors == msi_config->total_vectors) {
989c41a6700SCarl Huang 		set_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags);
990c41a6700SCarl Huang 		ab_pci->irq_flags = IRQF_SHARED;
991ac6e7348SCarl Huang 	} else {
992ac6e7348SCarl Huang 		num_vectors = pci_alloc_irq_vectors(ab_pci->pdev,
993ac6e7348SCarl Huang 						    1,
994ac6e7348SCarl Huang 						    1,
995ac6e7348SCarl Huang 						    PCI_IRQ_MSI);
996ac6e7348SCarl Huang 		if (num_vectors < 0) {
997ac6e7348SCarl Huang 			ret = -EINVAL;
998ac6e7348SCarl Huang 			goto reset_msi_config;
9995697a564SGovind Singh 		}
1000ac6e7348SCarl Huang 		clear_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags);
1001ac6e7348SCarl Huang 		ab_pci->msi_config = &msi_config_one_msi;
1002ac6e7348SCarl Huang 		ab_pci->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;
1003ac6e7348SCarl Huang 		ath11k_dbg(ab, ATH11K_DBG_PCI, "request MSI one vector\n");
1004ac6e7348SCarl Huang 	}
1005ac6e7348SCarl Huang 	ath11k_info(ab, "MSI vectors: %d\n", num_vectors);
1006ac6e7348SCarl Huang 
100796527d52SBaochen Qiang 	ath11k_pci_msi_disable(ab_pci);
10085697a564SGovind Singh 
10095697a564SGovind Singh 	msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
10105697a564SGovind Singh 	if (!msi_desc) {
10115697a564SGovind Singh 		ath11k_err(ab, "msi_desc is NULL!\n");
10125697a564SGovind Singh 		ret = -EINVAL;
10135697a564SGovind Singh 		goto free_msi_vector;
10145697a564SGovind Singh 	}
10155697a564SGovind Singh 
10165697a564SGovind Singh 	ab_pci->msi_ep_base_data = msi_desc->msg.data;
1017e58f2259SThomas Gleixner 	if (msi_desc->pci.msi_attrib.is_64)
1018e8e55d89SAnilkumar Kolli 		set_bit(ATH11K_PCI_FLAG_IS_MSI_64, &ab_pci->flags);
10195697a564SGovind Singh 
10205697a564SGovind Singh 	ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab_pci->msi_ep_base_data);
10215697a564SGovind Singh 
10225697a564SGovind Singh 	return 0;
10235697a564SGovind Singh 
10245697a564SGovind Singh free_msi_vector:
10255697a564SGovind Singh 	pci_free_irq_vectors(ab_pci->pdev);
10265697a564SGovind Singh 
1027ac6e7348SCarl Huang reset_msi_config:
10285697a564SGovind Singh 	return ret;
10295697a564SGovind Singh }
10305697a564SGovind Singh 
103196527d52SBaochen Qiang static void ath11k_pci_free_msi(struct ath11k_pci *ab_pci)
10325697a564SGovind Singh {
10335697a564SGovind Singh 	pci_free_irq_vectors(ab_pci->pdev);
10345697a564SGovind Singh }
10355697a564SGovind Singh 
103687b4072dSCarl Huang static int ath11k_pci_config_msi_data(struct ath11k_pci *ab_pci)
103787b4072dSCarl Huang {
103887b4072dSCarl Huang 	struct msi_desc *msi_desc;
103987b4072dSCarl Huang 
104087b4072dSCarl Huang 	msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
104187b4072dSCarl Huang 	if (!msi_desc) {
104287b4072dSCarl Huang 		ath11k_err(ab_pci->ab, "msi_desc is NULL!\n");
104387b4072dSCarl Huang 		pci_free_irq_vectors(ab_pci->pdev);
104487b4072dSCarl Huang 		return -EINVAL;
104587b4072dSCarl Huang 	}
104687b4072dSCarl Huang 
104787b4072dSCarl Huang 	ab_pci->msi_ep_base_data = msi_desc->msg.data;
104887b4072dSCarl Huang 
104987b4072dSCarl Huang 	ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "pci after request_irq msi_ep_base_data %d\n",
105087b4072dSCarl Huang 		   ab_pci->msi_ep_base_data);
105187b4072dSCarl Huang 
105287b4072dSCarl Huang 	return 0;
105387b4072dSCarl Huang }
105487b4072dSCarl Huang 
10555762613eSGovind Singh static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev)
10565762613eSGovind Singh {
10575762613eSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
10585762613eSGovind Singh 	u16 device_id;
10595762613eSGovind Singh 	int ret = 0;
10605762613eSGovind Singh 
10615762613eSGovind Singh 	pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
10625762613eSGovind Singh 	if (device_id != ab_pci->dev_id)  {
10635762613eSGovind Singh 		ath11k_err(ab, "pci device id mismatch: 0x%x 0x%x\n",
10645762613eSGovind Singh 			   device_id, ab_pci->dev_id);
10655762613eSGovind Singh 		ret = -EIO;
10665762613eSGovind Singh 		goto out;
10675762613eSGovind Singh 	}
10685762613eSGovind Singh 
10695762613eSGovind Singh 	ret = pci_assign_resource(pdev, ATH11K_PCI_BAR_NUM);
10705762613eSGovind Singh 	if (ret) {
10715762613eSGovind Singh 		ath11k_err(ab, "failed to assign pci resource: %d\n", ret);
10725762613eSGovind Singh 		goto out;
10735762613eSGovind Singh 	}
10745762613eSGovind Singh 
10755762613eSGovind Singh 	ret = pci_enable_device(pdev);
10765762613eSGovind Singh 	if (ret) {
10775762613eSGovind Singh 		ath11k_err(ab, "failed to enable pci device: %d\n", ret);
10785762613eSGovind Singh 		goto out;
10795762613eSGovind Singh 	}
10805762613eSGovind Singh 
10815762613eSGovind Singh 	ret = pci_request_region(pdev, ATH11K_PCI_BAR_NUM, "ath11k_pci");
10825762613eSGovind Singh 	if (ret) {
10835762613eSGovind Singh 		ath11k_err(ab, "failed to request pci region: %d\n", ret);
10845762613eSGovind Singh 		goto disable_device;
10855762613eSGovind Singh 	}
10865762613eSGovind Singh 
1087923a1346SChristophe JAILLET 	ret = dma_set_mask_and_coherent(&pdev->dev,
1088923a1346SChristophe JAILLET 					DMA_BIT_MASK(ATH11K_PCI_DMA_MASK));
10895762613eSGovind Singh 	if (ret) {
10905762613eSGovind Singh 		ath11k_err(ab, "failed to set pci dma mask to %d: %d\n",
10915762613eSGovind Singh 			   ATH11K_PCI_DMA_MASK, ret);
10925762613eSGovind Singh 		goto release_region;
10935762613eSGovind Singh 	}
10945762613eSGovind Singh 
10955762613eSGovind Singh 	pci_set_master(pdev);
10965762613eSGovind Singh 
10975762613eSGovind Singh 	ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM);
10985762613eSGovind Singh 	ab->mem = pci_iomap(pdev, ATH11K_PCI_BAR_NUM, 0);
10995762613eSGovind Singh 	if (!ab->mem) {
11005762613eSGovind Singh 		ath11k_err(ab, "failed to map pci bar %d\n", ATH11K_PCI_BAR_NUM);
11015762613eSGovind Singh 		ret = -EIO;
11025762613eSGovind Singh 		goto clear_master;
11035762613eSGovind Singh 	}
11045762613eSGovind Singh 
11055762613eSGovind Singh 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem);
11065762613eSGovind Singh 	return 0;
11075762613eSGovind Singh 
11085762613eSGovind Singh clear_master:
11095762613eSGovind Singh 	pci_clear_master(pdev);
11105762613eSGovind Singh release_region:
11115762613eSGovind Singh 	pci_release_region(pdev, ATH11K_PCI_BAR_NUM);
11125762613eSGovind Singh disable_device:
11135762613eSGovind Singh 	pci_disable_device(pdev);
11145762613eSGovind Singh out:
11155762613eSGovind Singh 	return ret;
11165762613eSGovind Singh }
11175762613eSGovind Singh 
11185762613eSGovind Singh static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
11195762613eSGovind Singh {
11205762613eSGovind Singh 	struct ath11k_base *ab = ab_pci->ab;
11215762613eSGovind Singh 	struct pci_dev *pci_dev = ab_pci->pdev;
11225762613eSGovind Singh 
11235762613eSGovind Singh 	pci_iounmap(pci_dev, ab->mem);
11245762613eSGovind Singh 	ab->mem = NULL;
11255762613eSGovind Singh 	pci_clear_master(pci_dev);
11265762613eSGovind Singh 	pci_release_region(pci_dev, ATH11K_PCI_BAR_NUM);
11275762613eSGovind Singh 	if (pci_is_enabled(pci_dev))
11285762613eSGovind Singh 		pci_disable_device(pci_dev);
11295762613eSGovind Singh }
11305762613eSGovind Singh 
1131e9603f4bSCarl Huang static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
1132e9603f4bSCarl Huang {
1133e9603f4bSCarl Huang 	struct ath11k_base *ab = ab_pci->ab;
1134e9603f4bSCarl Huang 
1135e9603f4bSCarl Huang 	pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
1136e9603f4bSCarl Huang 				  &ab_pci->link_ctl);
1137e9603f4bSCarl Huang 
1138e9603f4bSCarl Huang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "pci link_ctl 0x%04x L0s %d L1 %d\n",
1139e9603f4bSCarl Huang 		   ab_pci->link_ctl,
1140e9603f4bSCarl Huang 		   u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
1141e9603f4bSCarl Huang 		   u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
1142e9603f4bSCarl Huang 
1143e9603f4bSCarl Huang 	/* disable L0s and L1 */
1144e9603f4bSCarl Huang 	pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
1145e9603f4bSCarl Huang 				   ab_pci->link_ctl & ~PCI_EXP_LNKCTL_ASPMC);
1146e9603f4bSCarl Huang 
1147e9603f4bSCarl Huang 	set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags);
1148e9603f4bSCarl Huang }
1149e9603f4bSCarl Huang 
1150e9603f4bSCarl Huang static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
1151e9603f4bSCarl Huang {
1152e9603f4bSCarl Huang 	if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags))
1153e9603f4bSCarl Huang 		pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
1154e9603f4bSCarl Huang 					   ab_pci->link_ctl);
1155e9603f4bSCarl Huang }
1156e9603f4bSCarl Huang 
11571399fb87SGovind Singh static int ath11k_pci_power_up(struct ath11k_base *ab)
11581399fb87SGovind Singh {
11591399fb87SGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
11601399fb87SGovind Singh 	int ret;
11611399fb87SGovind Singh 
1162a05bd851SCarl Huang 	ab_pci->register_window = 0;
1163a05bd851SCarl Huang 	clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
1164babb0cedSCarl Huang 	ath11k_pci_sw_reset(ab_pci->ab, true);
1165f3c603d4SCarl Huang 
1166e9603f4bSCarl Huang 	/* Disable ASPM during firmware download due to problems switching
1167e9603f4bSCarl Huang 	 * to AMSS state.
1168e9603f4bSCarl Huang 	 */
1169e9603f4bSCarl Huang 	ath11k_pci_aspm_disable(ab_pci);
1170e9603f4bSCarl Huang 
117196527d52SBaochen Qiang 	ath11k_pci_msi_enable(ab_pci);
117296527d52SBaochen Qiang 
11731399fb87SGovind Singh 	ret = ath11k_mhi_start(ab_pci);
11741399fb87SGovind Singh 	if (ret) {
11751399fb87SGovind Singh 		ath11k_err(ab, "failed to start mhi: %d\n", ret);
11761399fb87SGovind Singh 		return ret;
11771399fb87SGovind Singh 	}
11781399fb87SGovind Singh 
1179480a7361SKarthikeyan Periyasamy 	if (ab->bus_params.static_window_map)
1180480a7361SKarthikeyan Periyasamy 		ath11k_pci_select_static_window(ab_pci);
1181480a7361SKarthikeyan Periyasamy 
11821399fb87SGovind Singh 	return 0;
11831399fb87SGovind Singh }
11841399fb87SGovind Singh 
11851399fb87SGovind Singh static void ath11k_pci_power_down(struct ath11k_base *ab)
11861399fb87SGovind Singh {
11871399fb87SGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
11881399fb87SGovind Singh 
1189e9603f4bSCarl Huang 	/* restore aspm in case firmware bootup fails */
1190e9603f4bSCarl Huang 	ath11k_pci_aspm_restore(ab_pci);
1191e9603f4bSCarl Huang 
1192babb0cedSCarl Huang 	ath11k_pci_force_wake(ab_pci->ab);
119396527d52SBaochen Qiang 
119496527d52SBaochen Qiang 	ath11k_pci_msi_disable(ab_pci);
119596527d52SBaochen Qiang 
11961399fb87SGovind Singh 	ath11k_mhi_stop(ab_pci);
1197a05bd851SCarl Huang 	clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
1198babb0cedSCarl Huang 	ath11k_pci_sw_reset(ab_pci->ab, false);
11991399fb87SGovind Singh }
12001399fb87SGovind Singh 
1201fa5917e4SCarl Huang static int ath11k_pci_hif_suspend(struct ath11k_base *ab)
1202fa5917e4SCarl Huang {
1203fa5917e4SCarl Huang 	struct ath11k_pci *ar_pci = ath11k_pci_priv(ab);
1204fa5917e4SCarl Huang 
1205fa5917e4SCarl Huang 	ath11k_mhi_suspend(ar_pci);
1206fa5917e4SCarl Huang 
1207fa5917e4SCarl Huang 	return 0;
1208fa5917e4SCarl Huang }
1209fa5917e4SCarl Huang 
1210fa5917e4SCarl Huang static int ath11k_pci_hif_resume(struct ath11k_base *ab)
1211fa5917e4SCarl Huang {
1212fa5917e4SCarl Huang 	struct ath11k_pci *ar_pci = ath11k_pci_priv(ab);
1213fa5917e4SCarl Huang 
1214fa5917e4SCarl Huang 	ath11k_mhi_resume(ar_pci);
1215fa5917e4SCarl Huang 
1216fa5917e4SCarl Huang 	return 0;
1217fa5917e4SCarl Huang }
1218fa5917e4SCarl Huang 
12192c3960c2SGovind Singh static void ath11k_pci_kill_tasklets(struct ath11k_base *ab)
12202c3960c2SGovind Singh {
12212c3960c2SGovind Singh 	int i;
12222c3960c2SGovind Singh 
1223d9d4b5f3SKalle Valo 	for (i = 0; i < ab->hw_params.ce_count; i++) {
12242c3960c2SGovind Singh 		struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
12252c3960c2SGovind Singh 
1226e3396b8bSCarl Huang 		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
12272c3960c2SGovind Singh 			continue;
12282c3960c2SGovind Singh 
12292c3960c2SGovind Singh 		tasklet_kill(&ce_pipe->intr_tq);
12302c3960c2SGovind Singh 	}
12312c3960c2SGovind Singh }
12322c3960c2SGovind Singh 
1233d578ec2aSCarl Huang static void ath11k_pci_ce_irq_disable_sync(struct ath11k_base *ab)
12347f4beda2SGovind Singh {
12352c3960c2SGovind Singh 	ath11k_pci_ce_irqs_disable(ab);
12362c3960c2SGovind Singh 	ath11k_pci_sync_ce_irqs(ab);
12372c3960c2SGovind Singh 	ath11k_pci_kill_tasklets(ab);
1238d578ec2aSCarl Huang }
1239d578ec2aSCarl Huang 
1240d578ec2aSCarl Huang static void ath11k_pci_stop(struct ath11k_base *ab)
1241d578ec2aSCarl Huang {
1242d578ec2aSCarl Huang 	ath11k_pci_ce_irq_disable_sync(ab);
12437f4beda2SGovind Singh 	ath11k_ce_cleanup_pipes(ab);
12447f4beda2SGovind Singh }
12457f4beda2SGovind Singh 
12467f4beda2SGovind Singh static int ath11k_pci_start(struct ath11k_base *ab)
12477f4beda2SGovind Singh {
1248a05bd851SCarl Huang 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
1249a05bd851SCarl Huang 
1250a05bd851SCarl Huang 	set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
1251a05bd851SCarl Huang 
1252915a081fSCarl Huang 	/* TODO: for now don't restore ASPM in case of single MSI
1253915a081fSCarl Huang 	 * vector as MHI register reading in M2 causes system hang.
1254915a081fSCarl Huang 	 */
1255915a081fSCarl Huang 	if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags))
1256e9603f4bSCarl Huang 		ath11k_pci_aspm_restore(ab_pci);
1257915a081fSCarl Huang 	else
1258915a081fSCarl Huang 		ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n");
1259e9603f4bSCarl Huang 
12607f4beda2SGovind Singh 	ath11k_pci_ce_irqs_enable(ab);
12612c3960c2SGovind Singh 	ath11k_ce_rx_post_buf(ab);
12622c3960c2SGovind Singh 
12632c3960c2SGovind Singh 	return 0;
12642c3960c2SGovind Singh }
12652c3960c2SGovind Singh 
1266d578ec2aSCarl Huang static void ath11k_pci_hif_ce_irq_enable(struct ath11k_base *ab)
1267d578ec2aSCarl Huang {
1268d578ec2aSCarl Huang 	ath11k_pci_ce_irqs_enable(ab);
1269d578ec2aSCarl Huang }
1270d578ec2aSCarl Huang 
1271d578ec2aSCarl Huang static void ath11k_pci_hif_ce_irq_disable(struct ath11k_base *ab)
1272d578ec2aSCarl Huang {
1273d578ec2aSCarl Huang 	ath11k_pci_ce_irq_disable_sync(ab);
1274d578ec2aSCarl Huang }
1275d578ec2aSCarl Huang 
12762c3960c2SGovind Singh static int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
12772c3960c2SGovind Singh 					  u8 *ul_pipe, u8 *dl_pipe)
12782c3960c2SGovind Singh {
12792c3960c2SGovind Singh 	const struct service_to_pipe *entry;
12802c3960c2SGovind Singh 	bool ul_set = false, dl_set = false;
12812c3960c2SGovind Singh 	int i;
12822c3960c2SGovind Singh 
1283967c1d11SAnilkumar Kolli 	for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {
1284967c1d11SAnilkumar Kolli 		entry = &ab->hw_params.svc_to_ce_map[i];
12852c3960c2SGovind Singh 
12862c3960c2SGovind Singh 		if (__le32_to_cpu(entry->service_id) != service_id)
12872c3960c2SGovind Singh 			continue;
12882c3960c2SGovind Singh 
12892c3960c2SGovind Singh 		switch (__le32_to_cpu(entry->pipedir)) {
12902c3960c2SGovind Singh 		case PIPEDIR_NONE:
12912c3960c2SGovind Singh 			break;
12922c3960c2SGovind Singh 		case PIPEDIR_IN:
12932c3960c2SGovind Singh 			WARN_ON(dl_set);
12942c3960c2SGovind Singh 			*dl_pipe = __le32_to_cpu(entry->pipenum);
12952c3960c2SGovind Singh 			dl_set = true;
12962c3960c2SGovind Singh 			break;
12972c3960c2SGovind Singh 		case PIPEDIR_OUT:
12982c3960c2SGovind Singh 			WARN_ON(ul_set);
12992c3960c2SGovind Singh 			*ul_pipe = __le32_to_cpu(entry->pipenum);
13002c3960c2SGovind Singh 			ul_set = true;
13012c3960c2SGovind Singh 			break;
13022c3960c2SGovind Singh 		case PIPEDIR_INOUT:
13032c3960c2SGovind Singh 			WARN_ON(dl_set);
13042c3960c2SGovind Singh 			WARN_ON(ul_set);
13052c3960c2SGovind Singh 			*dl_pipe = __le32_to_cpu(entry->pipenum);
13062c3960c2SGovind Singh 			*ul_pipe = __le32_to_cpu(entry->pipenum);
13072c3960c2SGovind Singh 			dl_set = true;
13082c3960c2SGovind Singh 			ul_set = true;
13092c3960c2SGovind Singh 			break;
13102c3960c2SGovind Singh 		}
13112c3960c2SGovind Singh 	}
13122c3960c2SGovind Singh 
13132c3960c2SGovind Singh 	if (WARN_ON(!ul_set || !dl_set))
13142c3960c2SGovind Singh 		return -ENOENT;
13157f4beda2SGovind Singh 
13167f4beda2SGovind Singh 	return 0;
13177f4beda2SGovind Singh }
13187f4beda2SGovind Singh 
13197f4beda2SGovind Singh static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
13207f4beda2SGovind Singh 	.start = ath11k_pci_start,
13217f4beda2SGovind Singh 	.stop = ath11k_pci_stop,
1322654e959aSGovind Singh 	.read32 = ath11k_pci_read32,
1323654e959aSGovind Singh 	.write32 = ath11k_pci_write32,
13241399fb87SGovind Singh 	.power_down = ath11k_pci_power_down,
13251399fb87SGovind Singh 	.power_up = ath11k_pci_power_up,
1326fa5917e4SCarl Huang 	.suspend = ath11k_pci_hif_suspend,
1327fa5917e4SCarl Huang 	.resume = ath11k_pci_hif_resume,
1328d4ecb90bSCarl Huang 	.irq_enable = ath11k_pci_ext_irq_enable,
1329d4ecb90bSCarl Huang 	.irq_disable = ath11k_pci_ext_irq_disable,
1330c4eacabeSGovind Singh 	.get_msi_address =  ath11k_pci_get_msi_address,
1331c4eacabeSGovind Singh 	.get_user_msi_vector = ath11k_get_user_msi_assignment,
13322c3960c2SGovind Singh 	.map_service_to_pipe = ath11k_pci_map_service_to_pipe,
1333d578ec2aSCarl Huang 	.ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
1334d578ec2aSCarl Huang 	.ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
13356289ac2bSKarthikeyan Periyasamy 	.get_ce_msi_idx = ath11k_pci_get_ce_msi_idx,
13361399fb87SGovind Singh };
13371399fb87SGovind Singh 
13380fbf1957SBaochen Qiang static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor)
13390fbf1957SBaochen Qiang {
13400fbf1957SBaochen Qiang 	u32 soc_hw_version;
13410fbf1957SBaochen Qiang 
13420fbf1957SBaochen Qiang 	soc_hw_version = ath11k_pci_read32(ab, TCSR_SOC_HW_VERSION);
13430fbf1957SBaochen Qiang 	*major = FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK,
13440fbf1957SBaochen Qiang 			   soc_hw_version);
13450fbf1957SBaochen Qiang 	*minor = FIELD_GET(TCSR_SOC_HW_VERSION_MINOR_MASK,
13460fbf1957SBaochen Qiang 			   soc_hw_version);
13470fbf1957SBaochen Qiang 
13480fbf1957SBaochen Qiang 	ath11k_dbg(ab, ATH11K_DBG_PCI, "pci tcsr_soc_hw_version major %d minor %d\n",
13490fbf1957SBaochen Qiang 		   *major, *minor);
13500fbf1957SBaochen Qiang }
13510fbf1957SBaochen Qiang 
13526e0355afSGovind Singh static int ath11k_pci_probe(struct pci_dev *pdev,
13536e0355afSGovind Singh 			    const struct pci_device_id *pci_dev)
13546e0355afSGovind Singh {
13556e0355afSGovind Singh 	struct ath11k_base *ab;
13565762613eSGovind Singh 	struct ath11k_pci *ab_pci;
13576ac04bdcSAnilkumar Kolli 	u32 soc_hw_version_major, soc_hw_version_minor, addr;
13585762613eSGovind Singh 	int ret;
13596e0355afSGovind Singh 
13601ff8ed78SGovind Singh 	ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI,
13611ff8ed78SGovind Singh 			       &ath11k_pci_bus_params);
13626e0355afSGovind Singh 	if (!ab) {
13636e0355afSGovind Singh 		dev_err(&pdev->dev, "failed to allocate ath11k base\n");
13646e0355afSGovind Singh 		return -ENOMEM;
13656e0355afSGovind Singh 	}
13666e0355afSGovind Singh 
13676e0355afSGovind Singh 	ab->dev = &pdev->dev;
13686e0355afSGovind Singh 	pci_set_drvdata(pdev, ab);
13695762613eSGovind Singh 	ab_pci = ath11k_pci_priv(ab);
13705762613eSGovind Singh 	ab_pci->dev_id = pci_dev->device;
13715762613eSGovind Singh 	ab_pci->ab = ab;
13725697a564SGovind Singh 	ab_pci->pdev = pdev;
13737f4beda2SGovind Singh 	ab->hif.ops = &ath11k_pci_hif_ops;
13745762613eSGovind Singh 	pci_set_drvdata(pdev, ab);
1375654e959aSGovind Singh 	spin_lock_init(&ab_pci->window_lock);
13765762613eSGovind Singh 
13776ac04bdcSAnilkumar Kolli 	/* Set fixed_mem_region to true for platforms support reserved memory
13786ac04bdcSAnilkumar Kolli 	 * from DT. If memory is reserved from DT for FW, ath11k driver need not
13796ac04bdcSAnilkumar Kolli 	 * allocate memory.
13806ac04bdcSAnilkumar Kolli 	 */
13816ac04bdcSAnilkumar Kolli 	ret = of_property_read_u32(ab->dev->of_node, "memory-region", &addr);
13826ac04bdcSAnilkumar Kolli 	if (!ret)
13836ac04bdcSAnilkumar Kolli 		set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags);
13846ac04bdcSAnilkumar Kolli 
13855762613eSGovind Singh 	ret = ath11k_pci_claim(ab_pci, pdev);
13865762613eSGovind Singh 	if (ret) {
13875762613eSGovind Singh 		ath11k_err(ab, "failed to claim device: %d\n", ret);
13885762613eSGovind Singh 		goto err_free_core;
13895762613eSGovind Singh 	}
13906e0355afSGovind Singh 
1391fc95d10aSWen Gong 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "pci probe %04x:%04x %04x:%04x\n",
1392fc95d10aSWen Gong 		   pdev->vendor, pdev->device,
1393fc95d10aSWen Gong 		   pdev->subsystem_vendor, pdev->subsystem_device);
1394fc95d10aSWen Gong 
1395fc95d10aSWen Gong 	ab->id.vendor = pdev->vendor;
1396fc95d10aSWen Gong 	ab->id.device = pdev->device;
1397fc95d10aSWen Gong 	ab->id.subsystem_vendor = pdev->subsystem_vendor;
1398fc95d10aSWen Gong 	ab->id.subsystem_device = pdev->subsystem_device;
1399fc95d10aSWen Gong 
140018ac1665SKalle Valo 	switch (pci_dev->device) {
140118ac1665SKalle Valo 	case QCA6390_DEVICE_ID:
14020fbf1957SBaochen Qiang 		ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
14030fbf1957SBaochen Qiang 					   &soc_hw_version_minor);
140418ac1665SKalle Valo 		switch (soc_hw_version_major) {
140518ac1665SKalle Valo 		case 2:
140618ac1665SKalle Valo 			ab->hw_rev = ATH11K_HW_QCA6390_HW20;
140718ac1665SKalle Valo 			break;
140818ac1665SKalle Valo 		default:
140918ac1665SKalle Valo 			dev_err(&pdev->dev, "Unsupported QCA6390 SOC hardware version: %d %d\n",
141018ac1665SKalle Valo 				soc_hw_version_major, soc_hw_version_minor);
141118ac1665SKalle Valo 			ret = -EOPNOTSUPP;
141218ac1665SKalle Valo 			goto err_pci_free_region;
141318ac1665SKalle Valo 		}
14144e809461SAnilkumar Kolli 		ab_pci->msi_config = &ath11k_msi_config[0];
14154e809461SAnilkumar Kolli 		break;
14164e809461SAnilkumar Kolli 	case QCN9074_DEVICE_ID:
14174e809461SAnilkumar Kolli 		ab_pci->msi_config = &ath11k_msi_config[1];
14184e809461SAnilkumar Kolli 		ab->bus_params.static_window_map = true;
14194e809461SAnilkumar Kolli 		ab->hw_rev = ATH11K_HW_QCN9074_HW10;
142018ac1665SKalle Valo 		break;
14210fbf1957SBaochen Qiang 	case WCN6855_DEVICE_ID:
1422fc95d10aSWen Gong 		ab->id.bdf_search = ATH11K_BDF_SEARCH_BUS_AND_BOARD;
14230fbf1957SBaochen Qiang 		ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
14240fbf1957SBaochen Qiang 					   &soc_hw_version_minor);
14250fbf1957SBaochen Qiang 		switch (soc_hw_version_major) {
14260fbf1957SBaochen Qiang 		case 2:
1427d1147a31SBaochen Qiang 			switch (soc_hw_version_minor) {
1428d1147a31SBaochen Qiang 			case 0x00:
1429d1147a31SBaochen Qiang 			case 0x01:
14300fbf1957SBaochen Qiang 				ab->hw_rev = ATH11K_HW_WCN6855_HW20;
14310fbf1957SBaochen Qiang 				break;
1432d1147a31SBaochen Qiang 			case 0x10:
1433d1147a31SBaochen Qiang 			case 0x11:
1434d1147a31SBaochen Qiang 				ab->hw_rev = ATH11K_HW_WCN6855_HW21;
1435d1147a31SBaochen Qiang 				break;
14360fbf1957SBaochen Qiang 			default:
1437d1147a31SBaochen Qiang 				goto unsupported_wcn6855_soc;
1438d1147a31SBaochen Qiang 			}
1439d1147a31SBaochen Qiang 			break;
1440d1147a31SBaochen Qiang 		default:
1441d1147a31SBaochen Qiang unsupported_wcn6855_soc:
14420fbf1957SBaochen Qiang 			dev_err(&pdev->dev, "Unsupported WCN6855 SOC hardware version: %d %d\n",
14430fbf1957SBaochen Qiang 				soc_hw_version_major, soc_hw_version_minor);
14440fbf1957SBaochen Qiang 			ret = -EOPNOTSUPP;
14450fbf1957SBaochen Qiang 			goto err_pci_free_region;
14460fbf1957SBaochen Qiang 		}
14470fbf1957SBaochen Qiang 		ab_pci->msi_config = &ath11k_msi_config[0];
14480fbf1957SBaochen Qiang 		break;
144918ac1665SKalle Valo 	default:
145018ac1665SKalle Valo 		dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n",
145118ac1665SKalle Valo 			pci_dev->device);
145218ac1665SKalle Valo 		ret = -EOPNOTSUPP;
145318ac1665SKalle Valo 		goto err_pci_free_region;
145418ac1665SKalle Valo 	}
145518ac1665SKalle Valo 
145696527d52SBaochen Qiang 	ret = ath11k_pci_alloc_msi(ab_pci);
14575697a564SGovind Singh 	if (ret) {
14585697a564SGovind Singh 		ath11k_err(ab, "failed to enable msi: %d\n", ret);
14595697a564SGovind Singh 		goto err_pci_free_region;
14605697a564SGovind Singh 	}
14615697a564SGovind Singh 
1462b8246f88SKalle Valo 	ret = ath11k_core_pre_init(ab);
1463b8246f88SKalle Valo 	if (ret)
1464b8246f88SKalle Valo 		goto err_pci_disable_msi;
1465b8246f88SKalle Valo 
14661399fb87SGovind Singh 	ret = ath11k_mhi_register(ab_pci);
14671399fb87SGovind Singh 	if (ret) {
14681399fb87SGovind Singh 		ath11k_err(ab, "failed to register mhi: %d\n", ret);
14691399fb87SGovind Singh 		goto err_pci_disable_msi;
14701399fb87SGovind Singh 	}
14711399fb87SGovind Singh 
14727f4beda2SGovind Singh 	ret = ath11k_hal_srng_init(ab);
14737f4beda2SGovind Singh 	if (ret)
14747f4beda2SGovind Singh 		goto err_mhi_unregister;
14757f4beda2SGovind Singh 
14767f4beda2SGovind Singh 	ret = ath11k_ce_alloc_pipes(ab);
14777f4beda2SGovind Singh 	if (ret) {
14787f4beda2SGovind Singh 		ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret);
14797f4beda2SGovind Singh 		goto err_hal_srng_deinit;
14807f4beda2SGovind Singh 	}
14817f4beda2SGovind Singh 
14827f4beda2SGovind Singh 	ath11k_pci_init_qmi_ce_config(ab);
14837f4beda2SGovind Singh 
14847f4beda2SGovind Singh 	ret = ath11k_pci_config_irq(ab);
14857f4beda2SGovind Singh 	if (ret) {
14867f4beda2SGovind Singh 		ath11k_err(ab, "failed to config irq: %d\n", ret);
14877f4beda2SGovind Singh 		goto err_ce_free;
14887f4beda2SGovind Singh 	}
14897f4beda2SGovind Singh 
149087b4072dSCarl Huang 	/* kernel may allocate a dummy vector before request_irq and
149187b4072dSCarl Huang 	 * then allocate a real vector when request_irq is called.
149287b4072dSCarl Huang 	 * So get msi_data here again to avoid spurious interrupt
149387b4072dSCarl Huang 	 * as msi_data will configured to srngs.
149487b4072dSCarl Huang 	 */
149587b4072dSCarl Huang 	ret = ath11k_pci_config_msi_data(ab_pci);
149687b4072dSCarl Huang 	if (ret) {
149787b4072dSCarl Huang 		ath11k_err(ab, "failed to config msi_data: %d\n", ret);
149887b4072dSCarl Huang 		goto err_free_irq;
149987b4072dSCarl Huang 	}
150087b4072dSCarl Huang 
15017f4beda2SGovind Singh 	ret = ath11k_core_init(ab);
15027f4beda2SGovind Singh 	if (ret) {
15037f4beda2SGovind Singh 		ath11k_err(ab, "failed to init core: %d\n", ret);
15047f4beda2SGovind Singh 		goto err_free_irq;
15057f4beda2SGovind Singh 	}
15066e0355afSGovind Singh 	return 0;
15075762613eSGovind Singh 
15087f4beda2SGovind Singh err_free_irq:
15097f4beda2SGovind Singh 	ath11k_pci_free_irq(ab);
15107f4beda2SGovind Singh 
15117f4beda2SGovind Singh err_ce_free:
15127f4beda2SGovind Singh 	ath11k_ce_free_pipes(ab);
15137f4beda2SGovind Singh 
15147f4beda2SGovind Singh err_hal_srng_deinit:
15157f4beda2SGovind Singh 	ath11k_hal_srng_deinit(ab);
15167f4beda2SGovind Singh 
15177f4beda2SGovind Singh err_mhi_unregister:
15187f4beda2SGovind Singh 	ath11k_mhi_unregister(ab_pci);
15197f4beda2SGovind Singh 
1520b8246f88SKalle Valo err_pci_disable_msi:
152196527d52SBaochen Qiang 	ath11k_pci_free_msi(ab_pci);
1522b8246f88SKalle Valo 
15235697a564SGovind Singh err_pci_free_region:
15245697a564SGovind Singh 	ath11k_pci_free_region(ab_pci);
15255697a564SGovind Singh 
15265762613eSGovind Singh err_free_core:
15275762613eSGovind Singh 	ath11k_core_free(ab);
15285697a564SGovind Singh 
15295762613eSGovind Singh 	return ret;
15306e0355afSGovind Singh }
15316e0355afSGovind Singh 
15326e0355afSGovind Singh static void ath11k_pci_remove(struct pci_dev *pdev)
15336e0355afSGovind Singh {
15346e0355afSGovind Singh 	struct ath11k_base *ab = pci_get_drvdata(pdev);
15355762613eSGovind Singh 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
15366e0355afSGovind Singh 
1537e94b0749SBaochen Qiang 	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
1538e94b0749SBaochen Qiang 
153961a57e51SAnilkumar Kolli 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
154061a57e51SAnilkumar Kolli 		ath11k_pci_power_down(ab);
154161a57e51SAnilkumar Kolli 		ath11k_debugfs_soc_destroy(ab);
154261a57e51SAnilkumar Kolli 		ath11k_qmi_deinit_service(ab);
154361a57e51SAnilkumar Kolli 		goto qmi_fail;
154461a57e51SAnilkumar Kolli 	}
154561a57e51SAnilkumar Kolli 
15466e0355afSGovind Singh 	set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
15476fbd8898SCarl Huang 
15486fbd8898SCarl Huang 	ath11k_core_deinit(ab);
15496fbd8898SCarl Huang 
155061a57e51SAnilkumar Kolli qmi_fail:
15511399fb87SGovind Singh 	ath11k_mhi_unregister(ab_pci);
15526fbd8898SCarl Huang 
15536fbd8898SCarl Huang 	ath11k_pci_free_irq(ab);
155496527d52SBaochen Qiang 	ath11k_pci_free_msi(ab_pci);
15555762613eSGovind Singh 	ath11k_pci_free_region(ab_pci);
15566fbd8898SCarl Huang 
15576fbd8898SCarl Huang 	ath11k_hal_srng_deinit(ab);
15586fbd8898SCarl Huang 	ath11k_ce_free_pipes(ab);
15596e0355afSGovind Singh 	ath11k_core_free(ab);
15606e0355afSGovind Singh }
15616e0355afSGovind Singh 
15621399fb87SGovind Singh static void ath11k_pci_shutdown(struct pci_dev *pdev)
15631399fb87SGovind Singh {
15641399fb87SGovind Singh 	struct ath11k_base *ab = pci_get_drvdata(pdev);
15651399fb87SGovind Singh 
15661399fb87SGovind Singh 	ath11k_pci_power_down(ab);
15671399fb87SGovind Singh }
15681399fb87SGovind Singh 
1569d1b0c338SCarl Huang static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
1570d1b0c338SCarl Huang {
1571d1b0c338SCarl Huang 	struct ath11k_base *ab = dev_get_drvdata(dev);
1572d1b0c338SCarl Huang 	int ret;
1573d1b0c338SCarl Huang 
1574*b4f4c564SKalle Valo 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
1575*b4f4c564SKalle Valo 		ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci suspend as qmi is not initialised\n");
1576*b4f4c564SKalle Valo 		return 0;
1577*b4f4c564SKalle Valo 	}
1578*b4f4c564SKalle Valo 
1579d1b0c338SCarl Huang 	ret = ath11k_core_suspend(ab);
1580d1b0c338SCarl Huang 	if (ret)
1581d1b0c338SCarl Huang 		ath11k_warn(ab, "failed to suspend core: %d\n", ret);
1582d1b0c338SCarl Huang 
1583d1b0c338SCarl Huang 	return ret;
1584d1b0c338SCarl Huang }
1585d1b0c338SCarl Huang 
1586d1b0c338SCarl Huang static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
1587d1b0c338SCarl Huang {
1588d1b0c338SCarl Huang 	struct ath11k_base *ab = dev_get_drvdata(dev);
1589d1b0c338SCarl Huang 	int ret;
1590d1b0c338SCarl Huang 
1591*b4f4c564SKalle Valo 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
1592*b4f4c564SKalle Valo 		ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci resume as qmi is not initialised\n");
1593*b4f4c564SKalle Valo 		return 0;
1594*b4f4c564SKalle Valo 	}
1595*b4f4c564SKalle Valo 
1596d1b0c338SCarl Huang 	ret = ath11k_core_resume(ab);
1597d1b0c338SCarl Huang 	if (ret)
1598d1b0c338SCarl Huang 		ath11k_warn(ab, "failed to resume core: %d\n", ret);
1599d1b0c338SCarl Huang 
1600d1b0c338SCarl Huang 	return ret;
1601d1b0c338SCarl Huang }
1602d1b0c338SCarl Huang 
1603d1b0c338SCarl Huang static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops,
1604d1b0c338SCarl Huang 			 ath11k_pci_pm_suspend,
1605d1b0c338SCarl Huang 			 ath11k_pci_pm_resume);
1606d1b0c338SCarl Huang 
16076e0355afSGovind Singh static struct pci_driver ath11k_pci_driver = {
16086e0355afSGovind Singh 	.name = "ath11k_pci",
16096e0355afSGovind Singh 	.id_table = ath11k_pci_id_table,
16106e0355afSGovind Singh 	.probe = ath11k_pci_probe,
16116e0355afSGovind Singh 	.remove = ath11k_pci_remove,
16121399fb87SGovind Singh 	.shutdown = ath11k_pci_shutdown,
1613d1b0c338SCarl Huang #ifdef CONFIG_PM
1614d1b0c338SCarl Huang 	.driver.pm = &ath11k_pci_pm_ops,
1615d1b0c338SCarl Huang #endif
16166e0355afSGovind Singh };
16176e0355afSGovind Singh 
16186e0355afSGovind Singh static int ath11k_pci_init(void)
16196e0355afSGovind Singh {
16206e0355afSGovind Singh 	int ret;
16216e0355afSGovind Singh 
16226e0355afSGovind Singh 	ret = pci_register_driver(&ath11k_pci_driver);
16236e0355afSGovind Singh 	if (ret)
16246e0355afSGovind Singh 		pr_err("failed to register ath11k pci driver: %d\n",
16256e0355afSGovind Singh 		       ret);
16266e0355afSGovind Singh 
16276e0355afSGovind Singh 	return ret;
16286e0355afSGovind Singh }
16296e0355afSGovind Singh module_init(ath11k_pci_init);
16306e0355afSGovind Singh 
16316e0355afSGovind Singh static void ath11k_pci_exit(void)
16326e0355afSGovind Singh {
16336e0355afSGovind Singh 	pci_unregister_driver(&ath11k_pci_driver);
16346e0355afSGovind Singh }
16356e0355afSGovind Singh 
16366e0355afSGovind Singh module_exit(ath11k_pci_exit);
16376e0355afSGovind Singh 
16386e0355afSGovind Singh MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN PCIe devices");
16396e0355afSGovind Singh MODULE_LICENSE("Dual BSD/GPL");
16403dbd7fe7SDevin Bayer 
16413dbd7fe7SDevin Bayer /* QCA639x 2.0 firmware files */
16423dbd7fe7SDevin Bayer MODULE_FIRMWARE(ATH11K_FW_DIR "/QCA6390/hw2.0/" ATH11K_BOARD_API2_FILE);
16433dbd7fe7SDevin Bayer MODULE_FIRMWARE(ATH11K_FW_DIR "/QCA6390/hw2.0/" ATH11K_AMSS_FILE);
16443dbd7fe7SDevin Bayer MODULE_FIRMWARE(ATH11K_FW_DIR "/QCA6390/hw2.0/" ATH11K_M3_FILE);
1645