Path: blob/main/sys/contrib/dev/athk/ath11k/pcic.c
105641 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.3* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.4* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.5*/67#include <linux/export.h>8#include "core.h"9#include "pcic.h"10#include "debug.h"1112static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {13"bhi",14"mhi-er0",15"mhi-er1",16"ce0",17"ce1",18"ce2",19"ce3",20"ce4",21"ce5",22"ce6",23"ce7",24"ce8",25"ce9",26"ce10",27"ce11",28"host2wbm-desc-feed",29"host2reo-re-injection",30"host2reo-command",31"host2rxdma-monitor-ring3",32"host2rxdma-monitor-ring2",33"host2rxdma-monitor-ring1",34"reo2ost-exception",35"wbm2host-rx-release",36"reo2host-status",37"reo2host-destination-ring4",38"reo2host-destination-ring3",39"reo2host-destination-ring2",40"reo2host-destination-ring1",41"rxdma2host-monitor-destination-mac3",42"rxdma2host-monitor-destination-mac2",43"rxdma2host-monitor-destination-mac1",44"ppdu-end-interrupts-mac3",45"ppdu-end-interrupts-mac2",46"ppdu-end-interrupts-mac1",47"rxdma2host-monitor-status-ring-mac3",48"rxdma2host-monitor-status-ring-mac2",49"rxdma2host-monitor-status-ring-mac1",50"host2rxdma-host-buf-ring-mac3",51"host2rxdma-host-buf-ring-mac2",52"host2rxdma-host-buf-ring-mac1",53"rxdma2host-destination-ring-mac3",54"rxdma2host-destination-ring-mac2",55"rxdma2host-destination-ring-mac1",56"host2tcl-input-ring4",57"host2tcl-input-ring3",58"host2tcl-input-ring2",59"host2tcl-input-ring1",60"wbm2host-tx-completions-ring3",61"wbm2host-tx-completions-ring2",62"wbm2host-tx-completions-ring1",63"tcl2host-status-ring",64};6566static const struct ath11k_msi_config ath11k_msi_config[] = {67{68.total_vectors = 32,69.total_users = 4,70.users = (struct ath11k_msi_user[]) {71{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },72{ .name = "CE", .num_vectors = 10, .base_vector = 3 },73{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },74{ .name = "DP", .num_vectors = 18, .base_vector = 14 },75},76.hw_rev = ATH11K_HW_QCA6390_HW20,77},78{79.total_vectors = 16,80.total_users = 3,81.users = (struct ath11k_msi_user[]) {82{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },83{ .name = "CE", .num_vectors = 5, .base_vector = 3 },84{ .name = "DP", .num_vectors = 8, .base_vector = 8 },85},86.hw_rev = ATH11K_HW_QCN9074_HW10,87},88{89.total_vectors = 32,90.total_users = 4,91.users = (struct ath11k_msi_user[]) {92{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },93{ .name = "CE", .num_vectors = 10, .base_vector = 3 },94{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },95{ .name = "DP", .num_vectors = 18, .base_vector = 14 },96},97.hw_rev = ATH11K_HW_WCN6855_HW20,98},99{100.total_vectors = 32,101.total_users = 4,102.users = (struct ath11k_msi_user[]) {103{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },104{ .name = "CE", .num_vectors = 10, .base_vector = 3 },105{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },106{ .name = "DP", .num_vectors = 18, .base_vector = 14 },107},108.hw_rev = ATH11K_HW_WCN6855_HW21,109},110{111.total_vectors = 28,112.total_users = 2,113.users = (struct ath11k_msi_user[]) {114{ .name = "CE", .num_vectors = 10, .base_vector = 0 },115{ .name = "DP", .num_vectors = 18, .base_vector = 10 },116},117.hw_rev = ATH11K_HW_WCN6750_HW10,118},119{120.total_vectors = 32,121.total_users = 4,122.users = (struct ath11k_msi_user[]) {123{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },124{ .name = "CE", .num_vectors = 10, .base_vector = 3 },125{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },126{ .name = "DP", .num_vectors = 18, .base_vector = 14 },127},128.hw_rev = ATH11K_HW_QCA2066_HW21,129},130{131.total_vectors = 32,132.total_users = 4,133.users = (struct ath11k_msi_user[]) {134{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },135{ .name = "CE", .num_vectors = 10, .base_vector = 3 },136{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },137{ .name = "DP", .num_vectors = 18, .base_vector = 14 },138},139.hw_rev = ATH11K_HW_QCA6698AQ_HW21,140},141};142143int ath11k_pcic_init_msi_config(struct ath11k_base *ab)144{145const struct ath11k_msi_config *msi_config;146int i;147148for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {149msi_config = &ath11k_msi_config[i];150151if (msi_config->hw_rev == ab->hw_rev)152break;153}154155if (i == ARRAY_SIZE(ath11k_msi_config)) {156ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",157ab->hw_rev);158return -EINVAL;159}160161ab->pci.msi.config = msi_config;162return 0;163}164EXPORT_SYMBOL(ath11k_pcic_init_msi_config);165166static void __ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)167{168if (offset < ATH11K_PCI_WINDOW_START)169#if defined(__linux__)170iowrite32(value, ab->mem + offset);171#elif defined(__FreeBSD__)172iowrite32(value, (char *)ab->mem + offset);173#endif174else175ab->pci.ops->window_write32(ab, offset, value);176}177178void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)179{180int ret = 0;181bool wakeup_required;182183/* for offset beyond BAR + 4K - 32, may184* need to wakeup the device to access.185*/186wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&187offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;188if (wakeup_required && ab->pci.ops->wakeup)189ret = ab->pci.ops->wakeup(ab);190191__ath11k_pcic_write32(ab, offset, value);192193if (wakeup_required && !ret && ab->pci.ops->release)194ab->pci.ops->release(ab);195}196EXPORT_SYMBOL(ath11k_pcic_write32);197198static u32 __ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)199{200u32 val;201202if (offset < ATH11K_PCI_WINDOW_START)203#if defined(__linux__)204val = ioread32(ab->mem + offset);205#elif defined(__FreeBSD__)206val = ioread32((char *)ab->mem + offset);207#endif208else209val = ab->pci.ops->window_read32(ab, offset);210211return val;212}213214u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)215{216int ret = 0;217u32 val;218bool wakeup_required;219220/* for offset beyond BAR + 4K - 32, may221* need to wakeup the device to access.222*/223wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&224offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;225if (wakeup_required && ab->pci.ops->wakeup)226ret = ab->pci.ops->wakeup(ab);227228val = __ath11k_pcic_read32(ab, offset);229230if (wakeup_required && !ret && ab->pci.ops->release)231ab->pci.ops->release(ab);232233return val;234}235EXPORT_SYMBOL(ath11k_pcic_read32);236237int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)238{239int ret = 0;240bool wakeup_required;241u32 *data = buf;242u32 i;243244/* for offset beyond BAR + 4K - 32, may245* need to wakeup the device to access.246*/247wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&248end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;249if (wakeup_required && ab->pci.ops->wakeup) {250ret = ab->pci.ops->wakeup(ab);251if (ret) {252ath11k_warn(ab,253"wakeup failed, data may be invalid: %d",254ret);255/* Even though wakeup() failed, continue processing rather256* than returning because some parts of the data may still257* be valid and useful in some cases, e.g. could give us258* some clues on firmware crash.259* Mislead due to invalid data could be avoided because we260* are aware of the wakeup failure.261*/262}263}264265for (i = start; i < end + 1; i += 4)266*data++ = __ath11k_pcic_read32(ab, i);267268if (wakeup_required && ab->pci.ops->release)269ab->pci.ops->release(ab);270271return 0;272}273EXPORT_SYMBOL(ath11k_pcic_read);274275void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,276u32 *msi_addr_hi)277{278*msi_addr_lo = ab->pci.msi.addr_lo;279*msi_addr_hi = ab->pci.msi.addr_hi;280}281EXPORT_SYMBOL(ath11k_pcic_get_msi_address);282283int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,284int *num_vectors, u32 *user_base_data,285u32 *base_vector)286{287const struct ath11k_msi_config *msi_config = ab->pci.msi.config;288int idx;289290for (idx = 0; idx < msi_config->total_users; idx++) {291if (strcmp(user_name, msi_config->users[idx].name) == 0) {292*num_vectors = msi_config->users[idx].num_vectors;293*base_vector = msi_config->users[idx].base_vector;294*user_base_data = *base_vector + ab->pci.msi.ep_base_data;295296ath11k_dbg(ab, ATH11K_DBG_PCI,297"msi assignment %s num_vectors %d user_base_data %u base_vector %u\n",298user_name, *num_vectors, *user_base_data,299*base_vector);300301return 0;302}303}304305ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);306307return -EINVAL;308}309EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment);310311void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)312{313u32 i, msi_data_idx;314315for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {316if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)317continue;318319if (ce_id == i)320break;321322msi_data_idx++;323}324*msi_idx = msi_data_idx;325}326EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx);327328static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)329{330int i, j;331332for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {333struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];334335for (j = 0; j < irq_grp->num_irq; j++)336free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);337338netif_napi_del(&irq_grp->napi);339free_netdev(irq_grp->napi_ndev);340}341}342343void ath11k_pcic_free_irq(struct ath11k_base *ab)344{345int i, irq_idx;346347for (i = 0; i < ab->hw_params.ce_count; i++) {348if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)349continue;350irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;351free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);352}353354ath11k_pcic_free_ext_irq(ab);355}356EXPORT_SYMBOL(ath11k_pcic_free_irq);357358static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)359{360u32 irq_idx;361362/* In case of one MSI vector, we handle irq enable/disable in a363* uniform way since we only have one irq364*/365if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))366return;367368irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;369enable_irq(ab->irq_num[irq_idx]);370}371372static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)373{374u32 irq_idx;375376/* In case of one MSI vector, we handle irq enable/disable in a377* uniform way since we only have one irq378*/379if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))380return;381382irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;383disable_irq_nosync(ab->irq_num[irq_idx]);384}385386static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)387{388int i;389390clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);391392for (i = 0; i < ab->hw_params.ce_count; i++) {393if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)394continue;395ath11k_pcic_ce_irq_disable(ab, i);396}397}398399static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)400{401int i;402int irq_idx;403404for (i = 0; i < ab->hw_params.ce_count; i++) {405if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)406continue;407408irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;409synchronize_irq(ab->irq_num[irq_idx]);410}411}412413static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)414{415struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);416int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;417418ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);419420enable_irq(ce_pipe->ab->irq_num[irq_idx]);421}422423static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)424{425struct ath11k_ce_pipe *ce_pipe = arg;426struct ath11k_base *ab = ce_pipe->ab;427int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;428429if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))430return IRQ_HANDLED;431432/* last interrupt received for this CE */433ce_pipe->timestamp = jiffies;434435disable_irq_nosync(ab->irq_num[irq_idx]);436437tasklet_schedule(&ce_pipe->intr_tq);438439return IRQ_HANDLED;440}441442static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)443{444struct ath11k_base *ab = irq_grp->ab;445int i;446447/* In case of one MSI vector, we handle irq enable/disable448* in a uniform way since we only have one irq449*/450if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))451return;452453for (i = 0; i < irq_grp->num_irq; i++)454disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);455}456457static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)458{459int i;460461clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);462463for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {464struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];465466ath11k_pcic_ext_grp_disable(irq_grp);467468if (irq_grp->napi_enabled) {469napi_synchronize(&irq_grp->napi);470napi_disable(&irq_grp->napi);471irq_grp->napi_enabled = false;472}473}474}475476static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)477{478struct ath11k_base *ab = irq_grp->ab;479int i;480481/* In case of one MSI vector, we handle irq enable/disable in a482* uniform way since we only have one irq483*/484if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))485return;486487for (i = 0; i < irq_grp->num_irq; i++)488enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);489}490491void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)492{493int i;494495for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {496struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];497498if (!irq_grp->napi_enabled) {499napi_enable(&irq_grp->napi);500irq_grp->napi_enabled = true;501}502ath11k_pcic_ext_grp_enable(irq_grp);503}504505set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);506}507EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);508509static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)510{511int i, j, irq_idx;512513for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {514struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];515516for (j = 0; j < irq_grp->num_irq; j++) {517irq_idx = irq_grp->irqs[j];518synchronize_irq(ab->irq_num[irq_idx]);519}520}521}522523void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)524{525__ath11k_pcic_ext_irq_disable(ab);526ath11k_pcic_sync_ext_irqs(ab);527}528EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable);529530static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)531{532struct ath11k_ext_irq_grp *irq_grp = container_of(napi,533struct ath11k_ext_irq_grp,534napi);535struct ath11k_base *ab = irq_grp->ab;536int work_done;537int i;538539work_done = ath11k_dp_service_srng(ab, irq_grp, budget);540if (work_done < budget) {541napi_complete_done(napi, work_done);542for (i = 0; i < irq_grp->num_irq; i++)543enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);544}545546if (work_done > budget)547work_done = budget;548549return work_done;550}551552static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)553{554struct ath11k_ext_irq_grp *irq_grp = arg;555struct ath11k_base *ab = irq_grp->ab;556int i;557558if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))559return IRQ_HANDLED;560561ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq %d\n", irq);562563/* last interrupt received for this group */564irq_grp->timestamp = jiffies;565566for (i = 0; i < irq_grp->num_irq; i++)567disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);568569napi_schedule(&irq_grp->napi);570571return IRQ_HANDLED;572}573574static int575ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)576{577return ab->pci.ops->get_msi_irq(ab, vector);578}579580static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)581{582int i, j, n, ret, num_vectors = 0;583u32 user_base_data = 0, base_vector = 0;584struct ath11k_ext_irq_grp *irq_grp;585unsigned long irq_flags;586587ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,588&user_base_data,589&base_vector);590if (ret < 0)591return ret;592593irq_flags = IRQF_SHARED;594if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))595irq_flags |= IRQF_NOBALANCING;596597for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {598irq_grp = &ab->ext_irq_grp[i];599u32 num_irq = 0;600601irq_grp->ab = ab;602irq_grp->grp_id = i;603irq_grp->napi_ndev = alloc_netdev_dummy(0);604if (!irq_grp->napi_ndev) {605ret = -ENOMEM;606goto fail_allocate;607}608609netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,610ath11k_pcic_ext_grp_napi_poll);611612if (ab->hw_params.ring_mask->tx[i] ||613ab->hw_params.ring_mask->rx[i] ||614ab->hw_params.ring_mask->rx_err[i] ||615ab->hw_params.ring_mask->rx_wbm_rel[i] ||616ab->hw_params.ring_mask->reo_status[i] ||617ab->hw_params.ring_mask->rxdma2host[i] ||618ab->hw_params.ring_mask->host2rxdma[i] ||619ab->hw_params.ring_mask->rx_mon_status[i]) {620num_irq = 1;621}622623irq_grp->num_irq = num_irq;624irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;625626for (j = 0; j < irq_grp->num_irq; j++) {627int irq_idx = irq_grp->irqs[j];628int vector = (i % num_vectors) + base_vector;629int irq = ath11k_pcic_get_msi_irq(ab, vector);630631if (irq < 0) {632ret = irq;633goto fail_irq;634}635636ab->irq_num[irq_idx] = irq;637638ath11k_dbg(ab, ATH11K_DBG_PCI,639"irq %d group %d\n", irq, i);640641irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);642ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,643irq_flags, "DP_EXT_IRQ", irq_grp);644if (ret) {645ath11k_err(ab, "failed request irq %d: %d\n",646vector, ret);647for (n = 0; n <= i; n++) {648irq_grp = &ab->ext_irq_grp[n];649free_netdev(irq_grp->napi_ndev);650}651return ret;652}653}654ath11k_pcic_ext_grp_disable(irq_grp);655}656657return 0;658fail_irq:659/* i ->napi_ndev was properly allocated. Free it also */660i += 1;661fail_allocate:662for (n = 0; n < i; n++) {663irq_grp = &ab->ext_irq_grp[n];664free_netdev(irq_grp->napi_ndev);665}666return ret;667}668669int ath11k_pcic_config_irq(struct ath11k_base *ab)670{671struct ath11k_ce_pipe *ce_pipe;672u32 msi_data_start;673u32 msi_data_count, msi_data_idx;674u32 msi_irq_start;675unsigned int msi_data;676int irq, i, ret, irq_idx;677unsigned long irq_flags;678679ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,680&msi_data_start, &msi_irq_start);681if (ret)682return ret;683684irq_flags = IRQF_SHARED;685if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))686irq_flags |= IRQF_NOBALANCING;687688/* Configure CE irqs */689for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {690if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)691continue;692693msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;694irq = ath11k_pcic_get_msi_irq(ab, msi_data);695if (irq < 0)696return irq;697698ce_pipe = &ab->ce.ce_pipe[i];699700irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;701702tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);703704ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,705irq_flags, irq_name[irq_idx], ce_pipe);706if (ret) {707ath11k_err(ab, "failed to request irq %d: %d\n",708irq_idx, ret);709return ret;710}711712ab->irq_num[irq_idx] = irq;713msi_data_idx++;714715ath11k_pcic_ce_irq_disable(ab, i);716}717718ret = ath11k_pcic_ext_irq_config(ab);719if (ret)720return ret;721722return 0;723}724EXPORT_SYMBOL(ath11k_pcic_config_irq);725726void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)727{728int i;729730set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);731732for (i = 0; i < ab->hw_params.ce_count; i++) {733if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)734continue;735ath11k_pcic_ce_irq_enable(ab, i);736}737}738EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable);739740static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)741{742int i;743744for (i = 0; i < ab->hw_params.ce_count; i++) {745struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];746747if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)748continue;749750tasklet_kill(&ce_pipe->intr_tq);751}752}753754void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)755{756ath11k_pcic_ce_irqs_disable(ab);757ath11k_pcic_sync_ce_irqs(ab);758ath11k_pcic_kill_tasklets(ab);759}760EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync);761762void ath11k_pcic_stop(struct ath11k_base *ab)763{764ath11k_pcic_ce_irq_disable_sync(ab);765ath11k_ce_cleanup_pipes(ab);766}767EXPORT_SYMBOL(ath11k_pcic_stop);768769int ath11k_pcic_start(struct ath11k_base *ab)770{771set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);772773ath11k_pcic_ce_irqs_enable(ab);774ath11k_ce_rx_post_buf(ab);775776return 0;777}778EXPORT_SYMBOL(ath11k_pcic_start);779780int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,781u8 *ul_pipe, u8 *dl_pipe)782{783const struct service_to_pipe *entry;784bool ul_set = false, dl_set = false;785int i;786787for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {788entry = &ab->hw_params.svc_to_ce_map[i];789790if (__le32_to_cpu(entry->service_id) != service_id)791continue;792793switch (__le32_to_cpu(entry->pipedir)) {794case PIPEDIR_NONE:795break;796case PIPEDIR_IN:797WARN_ON(dl_set);798*dl_pipe = __le32_to_cpu(entry->pipenum);799dl_set = true;800break;801case PIPEDIR_OUT:802WARN_ON(ul_set);803*ul_pipe = __le32_to_cpu(entry->pipenum);804ul_set = true;805break;806case PIPEDIR_INOUT:807WARN_ON(dl_set);808WARN_ON(ul_set);809*dl_pipe = __le32_to_cpu(entry->pipenum);810*ul_pipe = __le32_to_cpu(entry->pipenum);811dl_set = true;812ul_set = true;813break;814}815}816817if (WARN_ON(!ul_set || !dl_set))818return -ENOENT;819820return 0;821}822EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe);823824int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,825const struct ath11k_pci_ops *pci_ops)826{827if (!pci_ops)828return 0;829830/* Return error if mandatory pci_ops callbacks are missing */831if (!pci_ops->get_msi_irq || !pci_ops->window_write32 ||832!pci_ops->window_read32)833return -EINVAL;834835ab->pci.ops = pci_ops;836return 0;837}838EXPORT_SYMBOL(ath11k_pcic_register_pci_ops);839840void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab)841{842int i;843844for (i = 0; i < ab->hw_params.ce_count; i++) {845if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||846i == ATH11K_PCI_CE_WAKE_IRQ)847continue;848ath11k_pcic_ce_irq_enable(ab, i);849}850}851EXPORT_SYMBOL(ath11k_pci_enable_ce_irqs_except_wake_irq);852853void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab)854{855int i;856int irq_idx;857struct ath11k_ce_pipe *ce_pipe;858859for (i = 0; i < ab->hw_params.ce_count; i++) {860ce_pipe = &ab->ce.ce_pipe[i];861irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;862863if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||864i == ATH11K_PCI_CE_WAKE_IRQ)865continue;866867disable_irq_nosync(ab->irq_num[irq_idx]);868synchronize_irq(ab->irq_num[irq_idx]);869tasklet_kill(&ce_pipe->intr_tq);870}871}872EXPORT_SYMBOL(ath11k_pci_disable_ce_irqs_except_wake_irq);873874875