853 lines · c
1// SPDX-License-Identifier: BSD-3-Clause-Clear2/*3 * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.4 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.5 */6 7#include "core.h"8#include "pcic.h"9#include "debug.h"10 11static 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 65static 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 },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 },118 {119 .total_vectors = 32,120 .total_users = 4,121 .users = (struct ath11k_msi_user[]) {122 { .name = "MHI", .num_vectors = 3, .base_vector = 0 },123 { .name = "CE", .num_vectors = 10, .base_vector = 3 },124 { .name = "WAKE", .num_vectors = 1, .base_vector = 13 },125 { .name = "DP", .num_vectors = 18, .base_vector = 14 },126 },127 .hw_rev = ATH11K_HW_QCA2066_HW21,128 },129};130 131int ath11k_pcic_init_msi_config(struct ath11k_base *ab)132{133 const struct ath11k_msi_config *msi_config;134 int i;135 136 for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {137 msi_config = &ath11k_msi_config[i];138 139 if (msi_config->hw_rev == ab->hw_rev)140 break;141 }142 143 if (i == ARRAY_SIZE(ath11k_msi_config)) {144 ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",145 ab->hw_rev);146 return -EINVAL;147 }148 149 ab->pci.msi.config = msi_config;150 return 0;151}152EXPORT_SYMBOL(ath11k_pcic_init_msi_config);153 154static void __ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)155{156 if (offset < ATH11K_PCI_WINDOW_START)157 iowrite32(value, ab->mem + offset);158 else159 ab->pci.ops->window_write32(ab, offset, value);160}161 162void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)163{164 int ret = 0;165 bool wakeup_required;166 167 /* for offset beyond BAR + 4K - 32, may168 * need to wakeup the device to access.169 */170 wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&171 offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;172 if (wakeup_required && ab->pci.ops->wakeup)173 ret = ab->pci.ops->wakeup(ab);174 175 __ath11k_pcic_write32(ab, offset, value);176 177 if (wakeup_required && !ret && ab->pci.ops->release)178 ab->pci.ops->release(ab);179}180EXPORT_SYMBOL(ath11k_pcic_write32);181 182static u32 __ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)183{184 u32 val;185 186 if (offset < ATH11K_PCI_WINDOW_START)187 val = ioread32(ab->mem + offset);188 else189 val = ab->pci.ops->window_read32(ab, offset);190 191 return val;192}193 194u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)195{196 int ret = 0;197 u32 val;198 bool wakeup_required;199 200 /* for offset beyond BAR + 4K - 32, may201 * need to wakeup the device to access.202 */203 wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&204 offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;205 if (wakeup_required && ab->pci.ops->wakeup)206 ret = ab->pci.ops->wakeup(ab);207 208 val = __ath11k_pcic_read32(ab, offset);209 210 if (wakeup_required && !ret && ab->pci.ops->release)211 ab->pci.ops->release(ab);212 213 return val;214}215EXPORT_SYMBOL(ath11k_pcic_read32);216 217int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)218{219 int ret = 0;220 bool wakeup_required;221 u32 *data = buf;222 u32 i;223 224 /* for offset beyond BAR + 4K - 32, may225 * need to wakeup the device to access.226 */227 wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&228 end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;229 if (wakeup_required && ab->pci.ops->wakeup) {230 ret = ab->pci.ops->wakeup(ab);231 if (ret) {232 ath11k_warn(ab,233 "wakeup failed, data may be invalid: %d",234 ret);235 /* Even though wakeup() failed, continue processing rather236 * than returning because some parts of the data may still237 * be valid and useful in some cases, e.g. could give us238 * some clues on firmware crash.239 * Mislead due to invalid data could be avoided because we240 * are aware of the wakeup failure.241 */242 }243 }244 245 for (i = start; i < end + 1; i += 4)246 *data++ = __ath11k_pcic_read32(ab, i);247 248 if (wakeup_required && ab->pci.ops->release)249 ab->pci.ops->release(ab);250 251 return 0;252}253EXPORT_SYMBOL(ath11k_pcic_read);254 255void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,256 u32 *msi_addr_hi)257{258 *msi_addr_lo = ab->pci.msi.addr_lo;259 *msi_addr_hi = ab->pci.msi.addr_hi;260}261EXPORT_SYMBOL(ath11k_pcic_get_msi_address);262 263int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,264 int *num_vectors, u32 *user_base_data,265 u32 *base_vector)266{267 const struct ath11k_msi_config *msi_config = ab->pci.msi.config;268 int idx;269 270 for (idx = 0; idx < msi_config->total_users; idx++) {271 if (strcmp(user_name, msi_config->users[idx].name) == 0) {272 *num_vectors = msi_config->users[idx].num_vectors;273 *base_vector = msi_config->users[idx].base_vector;274 *user_base_data = *base_vector + ab->pci.msi.ep_base_data;275 276 ath11k_dbg(ab, ATH11K_DBG_PCI,277 "msi assignment %s num_vectors %d user_base_data %u base_vector %u\n",278 user_name, *num_vectors, *user_base_data,279 *base_vector);280 281 return 0;282 }283 }284 285 ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);286 287 return -EINVAL;288}289EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment);290 291void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)292{293 u32 i, msi_data_idx;294 295 for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {296 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)297 continue;298 299 if (ce_id == i)300 break;301 302 msi_data_idx++;303 }304 *msi_idx = msi_data_idx;305}306EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx);307 308static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)309{310 int i, j;311 312 for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {313 struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];314 315 for (j = 0; j < irq_grp->num_irq; j++)316 free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);317 318 netif_napi_del(&irq_grp->napi);319 free_netdev(irq_grp->napi_ndev);320 }321}322 323void ath11k_pcic_free_irq(struct ath11k_base *ab)324{325 int i, irq_idx;326 327 for (i = 0; i < ab->hw_params.ce_count; i++) {328 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)329 continue;330 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;331 free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);332 }333 334 ath11k_pcic_free_ext_irq(ab);335}336EXPORT_SYMBOL(ath11k_pcic_free_irq);337 338static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)339{340 u32 irq_idx;341 342 /* In case of one MSI vector, we handle irq enable/disable in a343 * uniform way since we only have one irq344 */345 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))346 return;347 348 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;349 enable_irq(ab->irq_num[irq_idx]);350}351 352static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)353{354 u32 irq_idx;355 356 /* In case of one MSI vector, we handle irq enable/disable in a357 * uniform way since we only have one irq358 */359 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))360 return;361 362 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;363 disable_irq_nosync(ab->irq_num[irq_idx]);364}365 366static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)367{368 int i;369 370 clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);371 372 for (i = 0; i < ab->hw_params.ce_count; i++) {373 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)374 continue;375 ath11k_pcic_ce_irq_disable(ab, i);376 }377}378 379static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)380{381 int i;382 int irq_idx;383 384 for (i = 0; i < ab->hw_params.ce_count; i++) {385 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)386 continue;387 388 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;389 synchronize_irq(ab->irq_num[irq_idx]);390 }391}392 393static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)394{395 struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);396 int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;397 398 ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);399 400 enable_irq(ce_pipe->ab->irq_num[irq_idx]);401}402 403static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)404{405 struct ath11k_ce_pipe *ce_pipe = arg;406 struct ath11k_base *ab = ce_pipe->ab;407 int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;408 409 if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))410 return IRQ_HANDLED;411 412 /* last interrupt received for this CE */413 ce_pipe->timestamp = jiffies;414 415 disable_irq_nosync(ab->irq_num[irq_idx]);416 417 tasklet_schedule(&ce_pipe->intr_tq);418 419 return IRQ_HANDLED;420}421 422static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)423{424 struct ath11k_base *ab = irq_grp->ab;425 int i;426 427 /* In case of one MSI vector, we handle irq enable/disable428 * in a uniform way since we only have one irq429 */430 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))431 return;432 433 for (i = 0; i < irq_grp->num_irq; i++)434 disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);435}436 437static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)438{439 int i;440 441 clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);442 443 for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {444 struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];445 446 ath11k_pcic_ext_grp_disable(irq_grp);447 448 if (irq_grp->napi_enabled) {449 napi_synchronize(&irq_grp->napi);450 napi_disable(&irq_grp->napi);451 irq_grp->napi_enabled = false;452 }453 }454}455 456static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)457{458 struct ath11k_base *ab = irq_grp->ab;459 int i;460 461 /* In case of one MSI vector, we handle irq enable/disable in a462 * uniform way since we only have one irq463 */464 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))465 return;466 467 for (i = 0; i < irq_grp->num_irq; i++)468 enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);469}470 471void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)472{473 int i;474 475 for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {476 struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];477 478 if (!irq_grp->napi_enabled) {479 napi_enable(&irq_grp->napi);480 irq_grp->napi_enabled = true;481 }482 ath11k_pcic_ext_grp_enable(irq_grp);483 }484 485 set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);486}487EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);488 489static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)490{491 int i, j, irq_idx;492 493 for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {494 struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];495 496 for (j = 0; j < irq_grp->num_irq; j++) {497 irq_idx = irq_grp->irqs[j];498 synchronize_irq(ab->irq_num[irq_idx]);499 }500 }501}502 503void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)504{505 __ath11k_pcic_ext_irq_disable(ab);506 ath11k_pcic_sync_ext_irqs(ab);507}508EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable);509 510static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)511{512 struct ath11k_ext_irq_grp *irq_grp = container_of(napi,513 struct ath11k_ext_irq_grp,514 napi);515 struct ath11k_base *ab = irq_grp->ab;516 int work_done;517 int i;518 519 work_done = ath11k_dp_service_srng(ab, irq_grp, budget);520 if (work_done < budget) {521 napi_complete_done(napi, work_done);522 for (i = 0; i < irq_grp->num_irq; i++)523 enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);524 }525 526 if (work_done > budget)527 work_done = budget;528 529 return work_done;530}531 532static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)533{534 struct ath11k_ext_irq_grp *irq_grp = arg;535 struct ath11k_base *ab = irq_grp->ab;536 int i;537 538 if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))539 return IRQ_HANDLED;540 541 ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq %d\n", irq);542 543 /* last interrupt received for this group */544 irq_grp->timestamp = jiffies;545 546 for (i = 0; i < irq_grp->num_irq; i++)547 disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);548 549 napi_schedule(&irq_grp->napi);550 551 return IRQ_HANDLED;552}553 554static int555ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)556{557 return ab->pci.ops->get_msi_irq(ab, vector);558}559 560static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)561{562 int i, j, n, ret, num_vectors = 0;563 u32 user_base_data = 0, base_vector = 0;564 struct ath11k_ext_irq_grp *irq_grp;565 unsigned long irq_flags;566 567 ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,568 &user_base_data,569 &base_vector);570 if (ret < 0)571 return ret;572 573 irq_flags = IRQF_SHARED;574 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))575 irq_flags |= IRQF_NOBALANCING;576 577 for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {578 irq_grp = &ab->ext_irq_grp[i];579 u32 num_irq = 0;580 581 irq_grp->ab = ab;582 irq_grp->grp_id = i;583 irq_grp->napi_ndev = alloc_netdev_dummy(0);584 if (!irq_grp->napi_ndev) {585 ret = -ENOMEM;586 goto fail_allocate;587 }588 589 netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,590 ath11k_pcic_ext_grp_napi_poll);591 592 if (ab->hw_params.ring_mask->tx[i] ||593 ab->hw_params.ring_mask->rx[i] ||594 ab->hw_params.ring_mask->rx_err[i] ||595 ab->hw_params.ring_mask->rx_wbm_rel[i] ||596 ab->hw_params.ring_mask->reo_status[i] ||597 ab->hw_params.ring_mask->rxdma2host[i] ||598 ab->hw_params.ring_mask->host2rxdma[i] ||599 ab->hw_params.ring_mask->rx_mon_status[i]) {600 num_irq = 1;601 }602 603 irq_grp->num_irq = num_irq;604 irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;605 606 for (j = 0; j < irq_grp->num_irq; j++) {607 int irq_idx = irq_grp->irqs[j];608 int vector = (i % num_vectors) + base_vector;609 int irq = ath11k_pcic_get_msi_irq(ab, vector);610 611 if (irq < 0) {612 ret = irq;613 goto fail_irq;614 }615 616 ab->irq_num[irq_idx] = irq;617 618 ath11k_dbg(ab, ATH11K_DBG_PCI,619 "irq %d group %d\n", irq, i);620 621 irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);622 ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,623 irq_flags, "DP_EXT_IRQ", irq_grp);624 if (ret) {625 ath11k_err(ab, "failed request irq %d: %d\n",626 vector, ret);627 for (n = 0; n <= i; n++) {628 irq_grp = &ab->ext_irq_grp[n];629 free_netdev(irq_grp->napi_ndev);630 }631 return ret;632 }633 }634 ath11k_pcic_ext_grp_disable(irq_grp);635 }636 637 return 0;638fail_irq:639 /* i ->napi_ndev was properly allocated. Free it also */640 i += 1;641fail_allocate:642 for (n = 0; n < i; n++) {643 irq_grp = &ab->ext_irq_grp[n];644 free_netdev(irq_grp->napi_ndev);645 }646 return ret;647}648 649int ath11k_pcic_config_irq(struct ath11k_base *ab)650{651 struct ath11k_ce_pipe *ce_pipe;652 u32 msi_data_start;653 u32 msi_data_count, msi_data_idx;654 u32 msi_irq_start;655 unsigned int msi_data;656 int irq, i, ret, irq_idx;657 unsigned long irq_flags;658 659 ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,660 &msi_data_start, &msi_irq_start);661 if (ret)662 return ret;663 664 irq_flags = IRQF_SHARED;665 if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))666 irq_flags |= IRQF_NOBALANCING;667 668 /* Configure CE irqs */669 for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {670 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)671 continue;672 673 msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;674 irq = ath11k_pcic_get_msi_irq(ab, msi_data);675 if (irq < 0)676 return irq;677 678 ce_pipe = &ab->ce.ce_pipe[i];679 680 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;681 682 tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);683 684 ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,685 irq_flags, irq_name[irq_idx], ce_pipe);686 if (ret) {687 ath11k_err(ab, "failed to request irq %d: %d\n",688 irq_idx, ret);689 return ret;690 }691 692 ab->irq_num[irq_idx] = irq;693 msi_data_idx++;694 695 ath11k_pcic_ce_irq_disable(ab, i);696 }697 698 ret = ath11k_pcic_ext_irq_config(ab);699 if (ret)700 return ret;701 702 return 0;703}704EXPORT_SYMBOL(ath11k_pcic_config_irq);705 706void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)707{708 int i;709 710 set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);711 712 for (i = 0; i < ab->hw_params.ce_count; i++) {713 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)714 continue;715 ath11k_pcic_ce_irq_enable(ab, i);716 }717}718EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable);719 720static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)721{722 int i;723 724 for (i = 0; i < ab->hw_params.ce_count; i++) {725 struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];726 727 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)728 continue;729 730 tasklet_kill(&ce_pipe->intr_tq);731 }732}733 734void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)735{736 ath11k_pcic_ce_irqs_disable(ab);737 ath11k_pcic_sync_ce_irqs(ab);738 ath11k_pcic_kill_tasklets(ab);739}740EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync);741 742void ath11k_pcic_stop(struct ath11k_base *ab)743{744 ath11k_pcic_ce_irq_disable_sync(ab);745 ath11k_ce_cleanup_pipes(ab);746}747EXPORT_SYMBOL(ath11k_pcic_stop);748 749int ath11k_pcic_start(struct ath11k_base *ab)750{751 set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);752 753 ath11k_pcic_ce_irqs_enable(ab);754 ath11k_ce_rx_post_buf(ab);755 756 return 0;757}758EXPORT_SYMBOL(ath11k_pcic_start);759 760int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,761 u8 *ul_pipe, u8 *dl_pipe)762{763 const struct service_to_pipe *entry;764 bool ul_set = false, dl_set = false;765 int i;766 767 for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {768 entry = &ab->hw_params.svc_to_ce_map[i];769 770 if (__le32_to_cpu(entry->service_id) != service_id)771 continue;772 773 switch (__le32_to_cpu(entry->pipedir)) {774 case PIPEDIR_NONE:775 break;776 case PIPEDIR_IN:777 WARN_ON(dl_set);778 *dl_pipe = __le32_to_cpu(entry->pipenum);779 dl_set = true;780 break;781 case PIPEDIR_OUT:782 WARN_ON(ul_set);783 *ul_pipe = __le32_to_cpu(entry->pipenum);784 ul_set = true;785 break;786 case PIPEDIR_INOUT:787 WARN_ON(dl_set);788 WARN_ON(ul_set);789 *dl_pipe = __le32_to_cpu(entry->pipenum);790 *ul_pipe = __le32_to_cpu(entry->pipenum);791 dl_set = true;792 ul_set = true;793 break;794 }795 }796 797 if (WARN_ON(!ul_set || !dl_set))798 return -ENOENT;799 800 return 0;801}802EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe);803 804int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,805 const struct ath11k_pci_ops *pci_ops)806{807 if (!pci_ops)808 return 0;809 810 /* Return error if mandatory pci_ops callbacks are missing */811 if (!pci_ops->get_msi_irq || !pci_ops->window_write32 ||812 !pci_ops->window_read32)813 return -EINVAL;814 815 ab->pci.ops = pci_ops;816 return 0;817}818EXPORT_SYMBOL(ath11k_pcic_register_pci_ops);819 820void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab)821{822 int i;823 824 for (i = 0; i < ab->hw_params.ce_count; i++) {825 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||826 i == ATH11K_PCI_CE_WAKE_IRQ)827 continue;828 ath11k_pcic_ce_irq_enable(ab, i);829 }830}831EXPORT_SYMBOL(ath11k_pci_enable_ce_irqs_except_wake_irq);832 833void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab)834{835 int i;836 int irq_idx;837 struct ath11k_ce_pipe *ce_pipe;838 839 for (i = 0; i < ab->hw_params.ce_count; i++) {840 ce_pipe = &ab->ce.ce_pipe[i];841 irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;842 843 if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||844 i == ATH11K_PCI_CE_WAKE_IRQ)845 continue;846 847 disable_irq_nosync(ab->irq_num[irq_idx]);848 synchronize_irq(ab->irq_num[irq_idx]);849 tasklet_kill(&ce_pipe->intr_tq);850 }851}852EXPORT_SYMBOL(ath11k_pci_disable_ce_irqs_except_wake_irq);853