Commit | Line | Data |
---|---|---|
bbfdc5a7 MP |
1 | // SPDX-License-Identifier: BSD-3-Clause-Clear |
2 | /* | |
3 | * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. | |
4 | * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. | |
5 | */ | |
6 | ||
bbfdc5a7 MP |
7 | #include "core.h" |
8 | #include "pcic.h" | |
9 | #include "debug.h" | |
10 | ||
11 | static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { | |
12 | "bhi", | |
13 | "mhi-er0", | |
14 | "mhi-er1", | |
15 | "ce0", | |
16 | "ce1", | |
17 | "ce2", | |
18 | "ce3", | |
19 | "ce4", | |
20 | "ce5", | |
21 | "ce6", | |
22 | "ce7", | |
23 | "ce8", | |
24 | "ce9", | |
25 | "ce10", | |
26 | "ce11", | |
27 | "host2wbm-desc-feed", | |
28 | "host2reo-re-injection", | |
29 | "host2reo-command", | |
30 | "host2rxdma-monitor-ring3", | |
31 | "host2rxdma-monitor-ring2", | |
32 | "host2rxdma-monitor-ring1", | |
33 | "reo2ost-exception", | |
34 | "wbm2host-rx-release", | |
35 | "reo2host-status", | |
36 | "reo2host-destination-ring4", | |
37 | "reo2host-destination-ring3", | |
38 | "reo2host-destination-ring2", | |
39 | "reo2host-destination-ring1", | |
40 | "rxdma2host-monitor-destination-mac3", | |
41 | "rxdma2host-monitor-destination-mac2", | |
42 | "rxdma2host-monitor-destination-mac1", | |
43 | "ppdu-end-interrupts-mac3", | |
44 | "ppdu-end-interrupts-mac2", | |
45 | "ppdu-end-interrupts-mac1", | |
46 | "rxdma2host-monitor-status-ring-mac3", | |
47 | "rxdma2host-monitor-status-ring-mac2", | |
48 | "rxdma2host-monitor-status-ring-mac1", | |
49 | "host2rxdma-host-buf-ring-mac3", | |
50 | "host2rxdma-host-buf-ring-mac2", | |
51 | "host2rxdma-host-buf-ring-mac1", | |
52 | "rxdma2host-destination-ring-mac3", | |
53 | "rxdma2host-destination-ring-mac2", | |
54 | "rxdma2host-destination-ring-mac1", | |
55 | "host2tcl-input-ring4", | |
56 | "host2tcl-input-ring3", | |
57 | "host2tcl-input-ring2", | |
58 | "host2tcl-input-ring1", | |
59 | "wbm2host-tx-completions-ring3", | |
60 | "wbm2host-tx-completions-ring2", | |
61 | "wbm2host-tx-completions-ring1", | |
62 | "tcl2host-status-ring", | |
63 | }; | |
64 | ||
8d06b802 MP |
65 | static const struct ath11k_msi_config ath11k_msi_config[] = { |
66 | { | |
67 | .total_vectors = 32, | |
68 | .total_users = 4, | |
69 | .users = (struct ath11k_msi_user[]) { | |
70 | { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, | |
71 | { .name = "CE", .num_vectors = 10, .base_vector = 3 }, | |
72 | { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, | |
73 | { .name = "DP", .num_vectors = 18, .base_vector = 14 }, | |
74 | }, | |
75 | .hw_rev = ATH11K_HW_QCA6390_HW20, | |
76 | }, | |
77 | { | |
78 | .total_vectors = 16, | |
79 | .total_users = 3, | |
80 | .users = (struct ath11k_msi_user[]) { | |
81 | { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, | |
82 | { .name = "CE", .num_vectors = 5, .base_vector = 3 }, | |
83 | { .name = "DP", .num_vectors = 8, .base_vector = 8 }, | |
84 | }, | |
85 | .hw_rev = ATH11K_HW_QCN9074_HW10, | |
86 | }, | |
87 | { | |
88 | .total_vectors = 32, | |
89 | .total_users = 4, | |
90 | .users = (struct ath11k_msi_user[]) { | |
91 | { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, | |
92 | { .name = "CE", .num_vectors = 10, .base_vector = 3 }, | |
93 | { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, | |
94 | { .name = "DP", .num_vectors = 18, .base_vector = 14 }, | |
95 | }, | |
96 | .hw_rev = ATH11K_HW_WCN6855_HW20, | |
97 | }, | |
98 | { | |
99 | .total_vectors = 32, | |
100 | .total_users = 4, | |
101 | .users = (struct ath11k_msi_user[]) { | |
102 | { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, | |
103 | { .name = "CE", .num_vectors = 10, .base_vector = 3 }, | |
104 | { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, | |
105 | { .name = "DP", .num_vectors = 18, .base_vector = 14 }, | |
106 | }, | |
107 | .hw_rev = ATH11K_HW_WCN6855_HW21, | |
108 | }, | |
00402f49 MP |
109 | { |
110 | .total_vectors = 28, | |
111 | .total_users = 2, | |
112 | .users = (struct ath11k_msi_user[]) { | |
113 | { .name = "CE", .num_vectors = 10, .base_vector = 0 }, | |
114 | { .name = "DP", .num_vectors = 18, .base_vector = 10 }, | |
115 | }, | |
116 | .hw_rev = ATH11K_HW_WCN6750_HW10, | |
117 | }, | |
8d06b802 MP |
118 | }; |
119 | ||
120 | int ath11k_pcic_init_msi_config(struct ath11k_base *ab) | |
121 | { | |
8d06b802 MP |
122 | const struct ath11k_msi_config *msi_config; |
123 | int i; | |
124 | ||
125 | for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) { | |
126 | msi_config = &ath11k_msi_config[i]; | |
127 | ||
128 | if (msi_config->hw_rev == ab->hw_rev) | |
129 | break; | |
130 | } | |
131 | ||
132 | if (i == ARRAY_SIZE(ath11k_msi_config)) { | |
133 | ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n", | |
134 | ab->hw_rev); | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
0cfaf224 | 138 | ab->pci.msi.config = msi_config; |
8d06b802 MP |
139 | return 0; |
140 | } | |
141 | EXPORT_SYMBOL(ath11k_pcic_init_msi_config); | |
142 | ||
bbfdc5a7 MP |
143 | void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value) |
144 | { | |
bbfdc5a7 MP |
145 | int ret = 0; |
146 | ||
147 | /* for offset beyond BAR + 4K - 32, may | |
5b32b6dd | 148 | * need to wakeup the device to access. |
bbfdc5a7 | 149 | */ |
5b32b6dd MP |
150 | if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && |
151 | offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup) | |
152 | ret = ab->pci.ops->wakeup(ab); | |
bbfdc5a7 | 153 | |
867f4eee | 154 | if (offset < ATH11K_PCI_WINDOW_START) |
bbfdc5a7 | 155 | iowrite32(value, ab->mem + offset); |
867f4eee | 156 | else |
56c8ccf3 | 157 | ab->pci.ops->window_write32(ab, offset, value); |
bbfdc5a7 | 158 | |
5b32b6dd MP |
159 | if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && |
160 | offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release && | |
bbfdc5a7 | 161 | !ret) |
5b32b6dd | 162 | ab->pci.ops->release(ab); |
bbfdc5a7 | 163 | } |
00402f49 | 164 | EXPORT_SYMBOL(ath11k_pcic_write32); |
bbfdc5a7 MP |
165 | |
166 | u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset) | |
167 | { | |
bbfdc5a7 | 168 | int ret = 0; |
867f4eee | 169 | u32 val; |
bbfdc5a7 MP |
170 | |
171 | /* for offset beyond BAR + 4K - 32, may | |
5b32b6dd | 172 | * need to wakeup the device to access. |
bbfdc5a7 | 173 | */ |
5b32b6dd MP |
174 | if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && |
175 | offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup) | |
176 | ret = ab->pci.ops->wakeup(ab); | |
bbfdc5a7 | 177 | |
867f4eee | 178 | if (offset < ATH11K_PCI_WINDOW_START) |
bbfdc5a7 | 179 | val = ioread32(ab->mem + offset); |
867f4eee | 180 | else |
56c8ccf3 | 181 | val = ab->pci.ops->window_read32(ab, offset); |
bbfdc5a7 | 182 | |
5b32b6dd MP |
183 | if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) && |
184 | offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release && | |
bbfdc5a7 | 185 | !ret) |
5b32b6dd | 186 | ab->pci.ops->release(ab); |
bbfdc5a7 MP |
187 | |
188 | return val; | |
189 | } | |
00402f49 | 190 | EXPORT_SYMBOL(ath11k_pcic_read32); |
bbfdc5a7 | 191 | |
bbfdc5a7 MP |
192 | void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, |
193 | u32 *msi_addr_hi) | |
194 | { | |
0cfaf224 MP |
195 | *msi_addr_lo = ab->pci.msi.addr_lo; |
196 | *msi_addr_hi = ab->pci.msi.addr_hi; | |
bbfdc5a7 | 197 | } |
00402f49 | 198 | EXPORT_SYMBOL(ath11k_pcic_get_msi_address); |
bbfdc5a7 | 199 | |
0cfaf224 | 200 | int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, |
bbfdc5a7 MP |
201 | int *num_vectors, u32 *user_base_data, |
202 | u32 *base_vector) | |
203 | { | |
0cfaf224 | 204 | const struct ath11k_msi_config *msi_config = ab->pci.msi.config; |
bbfdc5a7 MP |
205 | int idx; |
206 | ||
207 | for (idx = 0; idx < msi_config->total_users; idx++) { | |
208 | if (strcmp(user_name, msi_config->users[idx].name) == 0) { | |
209 | *num_vectors = msi_config->users[idx].num_vectors; | |
210 | *base_vector = msi_config->users[idx].base_vector; | |
0cfaf224 | 211 | *user_base_data = *base_vector + ab->pci.msi.ep_base_data; |
bbfdc5a7 MP |
212 | |
213 | ath11k_dbg(ab, ATH11K_DBG_PCI, | |
214 | "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", | |
215 | user_name, *num_vectors, *user_base_data, | |
216 | *base_vector); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | } | |
221 | ||
222 | ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name); | |
223 | ||
224 | return -EINVAL; | |
225 | } | |
00402f49 | 226 | EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment); |
bbfdc5a7 MP |
227 | |
228 | void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx) | |
229 | { | |
230 | u32 i, msi_data_idx; | |
231 | ||
232 | for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { | |
233 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
234 | continue; | |
235 | ||
236 | if (ce_id == i) | |
237 | break; | |
238 | ||
239 | msi_data_idx++; | |
240 | } | |
241 | *msi_idx = msi_data_idx; | |
242 | } | |
00402f49 | 243 | EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx); |
bbfdc5a7 | 244 | |
bbfdc5a7 MP |
245 | static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab) |
246 | { | |
247 | int i, j; | |
248 | ||
249 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
250 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
251 | ||
252 | for (j = 0; j < irq_grp->num_irq; j++) | |
253 | free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); | |
254 | ||
255 | netif_napi_del(&irq_grp->napi); | |
256 | } | |
257 | } | |
258 | ||
259 | void ath11k_pcic_free_irq(struct ath11k_base *ab) | |
260 | { | |
261 | int i, irq_idx; | |
262 | ||
263 | for (i = 0; i < ab->hw_params.ce_count; i++) { | |
264 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
265 | continue; | |
266 | irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; | |
267 | free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); | |
268 | } | |
269 | ||
270 | ath11k_pcic_free_ext_irq(ab); | |
271 | } | |
00402f49 | 272 | EXPORT_SYMBOL(ath11k_pcic_free_irq); |
bbfdc5a7 MP |
273 | |
274 | static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) | |
275 | { | |
bbfdc5a7 MP |
276 | u32 irq_idx; |
277 | ||
278 | /* In case of one MSI vector, we handle irq enable/disable in a | |
279 | * uniform way since we only have one irq | |
280 | */ | |
5b32b6dd | 281 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) |
bbfdc5a7 MP |
282 | return; |
283 | ||
284 | irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; | |
285 | enable_irq(ab->irq_num[irq_idx]); | |
286 | } | |
287 | ||
288 | static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) | |
289 | { | |
bbfdc5a7 MP |
290 | u32 irq_idx; |
291 | ||
292 | /* In case of one MSI vector, we handle irq enable/disable in a | |
293 | * uniform way since we only have one irq | |
294 | */ | |
5b32b6dd | 295 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) |
bbfdc5a7 MP |
296 | return; |
297 | ||
298 | irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; | |
299 | disable_irq_nosync(ab->irq_num[irq_idx]); | |
300 | } | |
301 | ||
302 | static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab) | |
303 | { | |
304 | int i; | |
305 | ||
306 | clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); | |
307 | ||
308 | for (i = 0; i < ab->hw_params.ce_count; i++) { | |
309 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
310 | continue; | |
311 | ath11k_pcic_ce_irq_disable(ab, i); | |
312 | } | |
313 | } | |
314 | ||
315 | static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab) | |
316 | { | |
317 | int i; | |
318 | int irq_idx; | |
319 | ||
320 | for (i = 0; i < ab->hw_params.ce_count; i++) { | |
321 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
322 | continue; | |
323 | ||
324 | irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; | |
325 | synchronize_irq(ab->irq_num[irq_idx]); | |
326 | } | |
327 | } | |
328 | ||
329 | static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t) | |
330 | { | |
331 | struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq); | |
332 | int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; | |
333 | ||
334 | ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); | |
335 | ||
336 | enable_irq(ce_pipe->ab->irq_num[irq_idx]); | |
337 | } | |
338 | ||
339 | static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg) | |
340 | { | |
341 | struct ath11k_ce_pipe *ce_pipe = arg; | |
342 | struct ath11k_base *ab = ce_pipe->ab; | |
343 | int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num; | |
344 | ||
345 | if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags)) | |
346 | return IRQ_HANDLED; | |
347 | ||
348 | /* last interrupt received for this CE */ | |
349 | ce_pipe->timestamp = jiffies; | |
350 | ||
351 | disable_irq_nosync(ab->irq_num[irq_idx]); | |
352 | ||
353 | tasklet_schedule(&ce_pipe->intr_tq); | |
354 | ||
355 | return IRQ_HANDLED; | |
356 | } | |
357 | ||
358 | static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) | |
359 | { | |
5b32b6dd | 360 | struct ath11k_base *ab = irq_grp->ab; |
bbfdc5a7 MP |
361 | int i; |
362 | ||
363 | /* In case of one MSI vector, we handle irq enable/disable | |
364 | * in a uniform way since we only have one irq | |
365 | */ | |
5b32b6dd | 366 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) |
bbfdc5a7 MP |
367 | return; |
368 | ||
369 | for (i = 0; i < irq_grp->num_irq; i++) | |
370 | disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
371 | } | |
372 | ||
373 | static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *sc) | |
374 | { | |
375 | int i; | |
376 | ||
377 | clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags); | |
378 | ||
379 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
380 | struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; | |
381 | ||
382 | ath11k_pcic_ext_grp_disable(irq_grp); | |
383 | ||
384 | if (irq_grp->napi_enabled) { | |
385 | napi_synchronize(&irq_grp->napi); | |
386 | napi_disable(&irq_grp->napi); | |
387 | irq_grp->napi_enabled = false; | |
388 | } | |
389 | } | |
390 | } | |
391 | ||
392 | static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) | |
393 | { | |
5b32b6dd | 394 | struct ath11k_base *ab = irq_grp->ab; |
bbfdc5a7 MP |
395 | int i; |
396 | ||
397 | /* In case of one MSI vector, we handle irq enable/disable in a | |
398 | * uniform way since we only have one irq | |
399 | */ | |
5b32b6dd | 400 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) |
bbfdc5a7 MP |
401 | return; |
402 | ||
403 | for (i = 0; i < irq_grp->num_irq; i++) | |
404 | enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
405 | } | |
406 | ||
407 | void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab) | |
408 | { | |
409 | int i; | |
410 | ||
411 | set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); | |
412 | ||
413 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
414 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
415 | ||
416 | if (!irq_grp->napi_enabled) { | |
417 | napi_enable(&irq_grp->napi); | |
418 | irq_grp->napi_enabled = true; | |
419 | } | |
420 | ath11k_pcic_ext_grp_enable(irq_grp); | |
421 | } | |
422 | } | |
00402f49 | 423 | EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable); |
bbfdc5a7 MP |
424 | |
425 | static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab) | |
426 | { | |
427 | int i, j, irq_idx; | |
428 | ||
429 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { | |
430 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
431 | ||
432 | for (j = 0; j < irq_grp->num_irq; j++) { | |
433 | irq_idx = irq_grp->irqs[j]; | |
434 | synchronize_irq(ab->irq_num[irq_idx]); | |
435 | } | |
436 | } | |
437 | } | |
438 | ||
439 | void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab) | |
440 | { | |
441 | __ath11k_pcic_ext_irq_disable(ab); | |
442 | ath11k_pcic_sync_ext_irqs(ab); | |
443 | } | |
00402f49 | 444 | EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable); |
bbfdc5a7 MP |
445 | |
446 | static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget) | |
447 | { | |
448 | struct ath11k_ext_irq_grp *irq_grp = container_of(napi, | |
449 | struct ath11k_ext_irq_grp, | |
450 | napi); | |
451 | struct ath11k_base *ab = irq_grp->ab; | |
452 | int work_done; | |
453 | int i; | |
454 | ||
455 | work_done = ath11k_dp_service_srng(ab, irq_grp, budget); | |
456 | if (work_done < budget) { | |
457 | napi_complete_done(napi, work_done); | |
458 | for (i = 0; i < irq_grp->num_irq; i++) | |
459 | enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
460 | } | |
461 | ||
462 | if (work_done > budget) | |
463 | work_done = budget; | |
464 | ||
465 | return work_done; | |
466 | } | |
467 | ||
468 | static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg) | |
469 | { | |
470 | struct ath11k_ext_irq_grp *irq_grp = arg; | |
471 | struct ath11k_base *ab = irq_grp->ab; | |
472 | int i; | |
473 | ||
474 | if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags)) | |
475 | return IRQ_HANDLED; | |
476 | ||
477 | ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq); | |
478 | ||
479 | /* last interrupt received for this group */ | |
480 | irq_grp->timestamp = jiffies; | |
481 | ||
482 | for (i = 0; i < irq_grp->num_irq; i++) | |
483 | disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); | |
484 | ||
485 | napi_schedule(&irq_grp->napi); | |
486 | ||
487 | return IRQ_HANDLED; | |
488 | } | |
489 | ||
5b32b6dd MP |
490 | static int |
491 | ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector) | |
492 | { | |
5b32b6dd MP |
493 | return ab->pci.ops->get_msi_irq(ab, vector); |
494 | } | |
495 | ||
bbfdc5a7 MP |
496 | static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) |
497 | { | |
bbfdc5a7 MP |
498 | int i, j, ret, num_vectors = 0; |
499 | u32 user_base_data = 0, base_vector = 0; | |
5b32b6dd | 500 | unsigned long irq_flags; |
bbfdc5a7 | 501 | |
0cfaf224 | 502 | ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, |
bbfdc5a7 MP |
503 | &user_base_data, |
504 | &base_vector); | |
505 | if (ret < 0) | |
506 | return ret; | |
507 | ||
5b32b6dd MP |
508 | irq_flags = IRQF_SHARED; |
509 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) | |
510 | irq_flags |= IRQF_NOBALANCING; | |
511 | ||
bbfdc5a7 MP |
512 | for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { |
513 | struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; | |
514 | u32 num_irq = 0; | |
515 | ||
516 | irq_grp->ab = ab; | |
517 | irq_grp->grp_id = i; | |
518 | init_dummy_netdev(&irq_grp->napi_ndev); | |
519 | netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, | |
b48b89f9 | 520 | ath11k_pcic_ext_grp_napi_poll); |
bbfdc5a7 MP |
521 | |
522 | if (ab->hw_params.ring_mask->tx[i] || | |
523 | ab->hw_params.ring_mask->rx[i] || | |
524 | ab->hw_params.ring_mask->rx_err[i] || | |
525 | ab->hw_params.ring_mask->rx_wbm_rel[i] || | |
526 | ab->hw_params.ring_mask->reo_status[i] || | |
527 | ab->hw_params.ring_mask->rxdma2host[i] || | |
528 | ab->hw_params.ring_mask->host2rxdma[i] || | |
529 | ab->hw_params.ring_mask->rx_mon_status[i]) { | |
530 | num_irq = 1; | |
531 | } | |
532 | ||
533 | irq_grp->num_irq = num_irq; | |
534 | irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i; | |
535 | ||
536 | for (j = 0; j < irq_grp->num_irq; j++) { | |
537 | int irq_idx = irq_grp->irqs[j]; | |
538 | int vector = (i % num_vectors) + base_vector; | |
5b32b6dd MP |
539 | int irq = ath11k_pcic_get_msi_irq(ab, vector); |
540 | ||
541 | if (irq < 0) | |
542 | return irq; | |
bbfdc5a7 MP |
543 | |
544 | ab->irq_num[irq_idx] = irq; | |
545 | ||
546 | ath11k_dbg(ab, ATH11K_DBG_PCI, | |
547 | "irq:%d group:%d\n", irq, i); | |
548 | ||
549 | irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); | |
550 | ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler, | |
5b32b6dd | 551 | irq_flags, "DP_EXT_IRQ", irq_grp); |
bbfdc5a7 MP |
552 | if (ret) { |
553 | ath11k_err(ab, "failed request irq %d: %d\n", | |
554 | vector, ret); | |
555 | return ret; | |
556 | } | |
557 | } | |
558 | ath11k_pcic_ext_grp_disable(irq_grp); | |
559 | } | |
560 | ||
561 | return 0; | |
562 | } | |
563 | ||
bbfdc5a7 MP |
564 | int ath11k_pcic_config_irq(struct ath11k_base *ab) |
565 | { | |
bbfdc5a7 MP |
566 | struct ath11k_ce_pipe *ce_pipe; |
567 | u32 msi_data_start; | |
568 | u32 msi_data_count, msi_data_idx; | |
569 | u32 msi_irq_start; | |
570 | unsigned int msi_data; | |
571 | int irq, i, ret, irq_idx; | |
5b32b6dd | 572 | unsigned long irq_flags; |
bbfdc5a7 | 573 | |
0cfaf224 | 574 | ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count, |
bbfdc5a7 MP |
575 | &msi_data_start, &msi_irq_start); |
576 | if (ret) | |
577 | return ret; | |
578 | ||
5b32b6dd MP |
579 | irq_flags = IRQF_SHARED; |
580 | if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) | |
581 | irq_flags |= IRQF_NOBALANCING; | |
bbfdc5a7 MP |
582 | |
583 | /* Configure CE irqs */ | |
584 | for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) { | |
585 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
586 | continue; | |
587 | ||
588 | msi_data = (msi_data_idx % msi_data_count) + msi_irq_start; | |
5b32b6dd MP |
589 | irq = ath11k_pcic_get_msi_irq(ab, msi_data); |
590 | if (irq < 0) | |
591 | return irq; | |
592 | ||
bbfdc5a7 MP |
593 | ce_pipe = &ab->ce.ce_pipe[i]; |
594 | ||
595 | irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; | |
596 | ||
597 | tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet); | |
598 | ||
599 | ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler, | |
5b32b6dd | 600 | irq_flags, irq_name[irq_idx], ce_pipe); |
bbfdc5a7 MP |
601 | if (ret) { |
602 | ath11k_err(ab, "failed to request irq %d: %d\n", | |
603 | irq_idx, ret); | |
5b32b6dd | 604 | return ret; |
bbfdc5a7 MP |
605 | } |
606 | ||
607 | ab->irq_num[irq_idx] = irq; | |
608 | msi_data_idx++; | |
609 | ||
610 | ath11k_pcic_ce_irq_disable(ab, i); | |
611 | } | |
612 | ||
613 | ret = ath11k_pcic_ext_irq_config(ab); | |
614 | if (ret) | |
5b32b6dd | 615 | return ret; |
bbfdc5a7 MP |
616 | |
617 | return 0; | |
bbfdc5a7 | 618 | } |
00402f49 | 619 | EXPORT_SYMBOL(ath11k_pcic_config_irq); |
bbfdc5a7 MP |
620 | |
621 | void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab) | |
622 | { | |
623 | int i; | |
624 | ||
625 | set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags); | |
626 | ||
627 | for (i = 0; i < ab->hw_params.ce_count; i++) { | |
628 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
629 | continue; | |
630 | ath11k_pcic_ce_irq_enable(ab, i); | |
631 | } | |
632 | } | |
00402f49 | 633 | EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable); |
bbfdc5a7 MP |
634 | |
635 | static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab) | |
636 | { | |
637 | int i; | |
638 | ||
639 | for (i = 0; i < ab->hw_params.ce_count; i++) { | |
640 | struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; | |
641 | ||
642 | if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) | |
643 | continue; | |
644 | ||
645 | tasklet_kill(&ce_pipe->intr_tq); | |
646 | } | |
647 | } | |
648 | ||
649 | void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab) | |
650 | { | |
651 | ath11k_pcic_ce_irqs_disable(ab); | |
652 | ath11k_pcic_sync_ce_irqs(ab); | |
653 | ath11k_pcic_kill_tasklets(ab); | |
654 | } | |
00402f49 | 655 | EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync); |
bbfdc5a7 MP |
656 | |
657 | void ath11k_pcic_stop(struct ath11k_base *ab) | |
658 | { | |
659 | ath11k_pcic_ce_irq_disable_sync(ab); | |
660 | ath11k_ce_cleanup_pipes(ab); | |
661 | } | |
00402f49 | 662 | EXPORT_SYMBOL(ath11k_pcic_stop); |
bbfdc5a7 MP |
663 | |
664 | int ath11k_pcic_start(struct ath11k_base *ab) | |
665 | { | |
5b32b6dd | 666 | set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); |
bbfdc5a7 MP |
667 | |
668 | ath11k_pcic_ce_irqs_enable(ab); | |
669 | ath11k_ce_rx_post_buf(ab); | |
670 | ||
671 | return 0; | |
672 | } | |
00402f49 | 673 | EXPORT_SYMBOL(ath11k_pcic_start); |
bbfdc5a7 MP |
674 | |
675 | int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, | |
676 | u8 *ul_pipe, u8 *dl_pipe) | |
677 | { | |
678 | const struct service_to_pipe *entry; | |
679 | bool ul_set = false, dl_set = false; | |
680 | int i; | |
681 | ||
682 | for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) { | |
683 | entry = &ab->hw_params.svc_to_ce_map[i]; | |
684 | ||
685 | if (__le32_to_cpu(entry->service_id) != service_id) | |
686 | continue; | |
687 | ||
688 | switch (__le32_to_cpu(entry->pipedir)) { | |
689 | case PIPEDIR_NONE: | |
690 | break; | |
691 | case PIPEDIR_IN: | |
692 | WARN_ON(dl_set); | |
693 | *dl_pipe = __le32_to_cpu(entry->pipenum); | |
694 | dl_set = true; | |
695 | break; | |
696 | case PIPEDIR_OUT: | |
697 | WARN_ON(ul_set); | |
698 | *ul_pipe = __le32_to_cpu(entry->pipenum); | |
699 | ul_set = true; | |
700 | break; | |
701 | case PIPEDIR_INOUT: | |
702 | WARN_ON(dl_set); | |
703 | WARN_ON(ul_set); | |
704 | *dl_pipe = __le32_to_cpu(entry->pipenum); | |
705 | *ul_pipe = __le32_to_cpu(entry->pipenum); | |
706 | dl_set = true; | |
707 | ul_set = true; | |
708 | break; | |
709 | } | |
710 | } | |
711 | ||
712 | if (WARN_ON(!ul_set || !dl_set)) | |
713 | return -ENOENT; | |
714 | ||
715 | return 0; | |
716 | } | |
00402f49 | 717 | EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe); |
867f4eee MP |
718 | |
719 | int ath11k_pcic_register_pci_ops(struct ath11k_base *ab, | |
720 | const struct ath11k_pci_ops *pci_ops) | |
721 | { | |
722 | if (!pci_ops) | |
723 | return 0; | |
724 | ||
725 | /* Return error if mandatory pci_ops callbacks are missing */ | |
726 | if (!pci_ops->get_msi_irq || !pci_ops->window_write32 || | |
727 | !pci_ops->window_read32) | |
728 | return -EINVAL; | |
729 | ||
730 | ab->pci.ops = pci_ops; | |
731 | return 0; | |
732 | } | |
733 | EXPORT_SYMBOL(ath11k_pcic_register_pci_ops); |