Path: blob/main/sys/contrib/dev/athk/ath11k/pcic.c
48375 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.3* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.4*/56#include "core.h"7#include "pcic.h"8#include "debug.h"910static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {11"bhi",12"mhi-er0",13"mhi-er1",14"ce0",15"ce1",16"ce2",17"ce3",18"ce4",19"ce5",20"ce6",21"ce7",22"ce8",23"ce9",24"ce10",25"ce11",26"host2wbm-desc-feed",27"host2reo-re-injection",28"host2reo-command",29"host2rxdma-monitor-ring3",30"host2rxdma-monitor-ring2",31"host2rxdma-monitor-ring1",32"reo2ost-exception",33"wbm2host-rx-release",34"reo2host-status",35"reo2host-destination-ring4",36"reo2host-destination-ring3",37"reo2host-destination-ring2",38"reo2host-destination-ring1",39"rxdma2host-monitor-destination-mac3",40"rxdma2host-monitor-destination-mac2",41"rxdma2host-monitor-destination-mac1",42"ppdu-end-interrupts-mac3",43"ppdu-end-interrupts-mac2",44"ppdu-end-interrupts-mac1",45"rxdma2host-monitor-status-ring-mac3",46"rxdma2host-monitor-status-ring-mac2",47"rxdma2host-monitor-status-ring-mac1",48"host2rxdma-host-buf-ring-mac3",49"host2rxdma-host-buf-ring-mac2",50"host2rxdma-host-buf-ring-mac1",51"rxdma2host-destination-ring-mac3",52"rxdma2host-destination-ring-mac2",53"rxdma2host-destination-ring-mac1",54"host2tcl-input-ring4",55"host2tcl-input-ring3",56"host2tcl-input-ring2",57"host2tcl-input-ring1",58"wbm2host-tx-completions-ring3",59"wbm2host-tx-completions-ring2",60"wbm2host-tx-completions-ring1",61"tcl2host-status-ring",62};6364static const struct ath11k_msi_config ath11k_msi_config[] = {65{66.total_vectors = 32,67.total_users = 4,68.users = (struct ath11k_msi_user[]) {69{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },70{ .name = "CE", .num_vectors = 10, .base_vector = 3 },71{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },72{ .name = "DP", .num_vectors = 18, .base_vector = 14 },73},74.hw_rev = ATH11K_HW_QCA6390_HW20,75},76{77.total_vectors = 16,78.total_users = 3,79.users = (struct ath11k_msi_user[]) {80{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },81{ .name = "CE", .num_vectors = 5, .base_vector = 3 },82{ .name = "DP", .num_vectors = 8, .base_vector = 8 },83},84.hw_rev = ATH11K_HW_QCN9074_HW10,85},86{87.total_vectors = 32,88.total_users = 4,89.users = (struct ath11k_msi_user[]) {90{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },91{ .name = "CE", .num_vectors = 10, .base_vector = 3 },92{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },93{ .name = "DP", .num_vectors = 18, .base_vector = 14 },94},95.hw_rev = ATH11K_HW_WCN6855_HW20,96},97{98.total_vectors = 32,99.total_users = 4,100.users = (struct ath11k_msi_user[]) {101{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },102{ .name = "CE", .num_vectors = 10, .base_vector = 3 },103{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },104{ .name = "DP", .num_vectors = 18, .base_vector = 14 },105},106.hw_rev = ATH11K_HW_WCN6855_HW21,107},108{109.total_vectors = 28,110.total_users = 2,111.users = (struct ath11k_msi_user[]) {112{ .name = "CE", .num_vectors = 10, .base_vector = 0 },113{ .name = "DP", .num_vectors = 18, .base_vector = 10 },114},115.hw_rev = ATH11K_HW_WCN6750_HW10,116},117};118119int ath11k_pcic_init_msi_config(struct ath11k_base *ab)120{121const struct ath11k_msi_config *msi_config;122int i;123124for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {125msi_config = &ath11k_msi_config[i];126127if (msi_config->hw_rev == ab->hw_rev)128break;129}130131if (i == ARRAY_SIZE(ath11k_msi_config)) {132ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",133ab->hw_rev);134return -EINVAL;135}136137ab->pci.msi.config = msi_config;138return 0;139}140EXPORT_SYMBOL(ath11k_pcic_init_msi_config);141142static void __ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)143{144if (offset < ATH11K_PCI_WINDOW_START)145#if defined(__linux__)146iowrite32(value, ab->mem + offset);147#elif defined(__FreeBSD__)148iowrite32(value, (char *)ab->mem + offset);149#endif150else151ab->pci.ops->window_write32(ab, offset, value);152}153154void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)155{156int ret = 0;157bool wakeup_required;158159/* for offset beyond BAR + 4K - 32, may160* need to wakeup the device to access.161*/162wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&163offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;164if (wakeup_required && ab->pci.ops->wakeup)165ret = ab->pci.ops->wakeup(ab);166167__ath11k_pcic_write32(ab, offset, value);168169if (wakeup_required && !ret && ab->pci.ops->release)170ab->pci.ops->release(ab);171}172EXPORT_SYMBOL(ath11k_pcic_write32);173174static u32 __ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)175{176u32 val;177178if (offset < ATH11K_PCI_WINDOW_START)179#if defined(__linux__)180val = ioread32(ab->mem + offset);181#elif defined(__FreeBSD__)182val = ioread32((char *)ab->mem + offset);183#endif184else185val = ab->pci.ops->window_read32(ab, offset);186187return val;188}189190u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)191{192int ret = 0;193u32 val;194bool wakeup_required;195196/* for offset beyond BAR + 4K - 32, may197* need to wakeup the device to access.198*/199wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&200offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;201if (wakeup_required && ab->pci.ops->wakeup)202ret = ab->pci.ops->wakeup(ab);203204val = __ath11k_pcic_read32(ab, offset);205206if (wakeup_required && !ret && ab->pci.ops->release)207ab->pci.ops->release(ab);208209return val;210}211EXPORT_SYMBOL(ath11k_pcic_read32);212213int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)214{215int ret = 0;216bool wakeup_required;217u32 *data = buf;218u32 i;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) &&224end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;225if (wakeup_required && ab->pci.ops->wakeup) {226ret = ab->pci.ops->wakeup(ab);227if (ret) {228ath11k_warn(ab,229"wakeup failed, data may be invalid: %d",230ret);231/* Even though wakeup() failed, continue processing rather232* than returning because some parts of the data may still233* be valid and useful in some cases, e.g. could give us234* some clues on firmware crash.235* Mislead due to invalid data could be avoided because we236* are aware of the wakeup failure.237*/238}239}240241for (i = start; i < end + 1; i += 4)242*data++ = __ath11k_pcic_read32(ab, i);243244if (wakeup_required && ab->pci.ops->release)245ab->pci.ops->release(ab);246247return 0;248}249EXPORT_SYMBOL(ath11k_pcic_read);250251void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,252u32 *msi_addr_hi)253{254*msi_addr_lo = ab->pci.msi.addr_lo;255*msi_addr_hi = ab->pci.msi.addr_hi;256}257EXPORT_SYMBOL(ath11k_pcic_get_msi_address);258259int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,260int *num_vectors, u32 *user_base_data,261u32 *base_vector)262{263const struct ath11k_msi_config *msi_config = ab->pci.msi.config;264int idx;265266for (idx = 0; idx < msi_config->total_users; idx++) {267if (strcmp(user_name, msi_config->users[idx].name) == 0) {268*num_vectors = msi_config->users[idx].num_vectors;269*base_vector = msi_config->users[idx].base_vector;270*user_base_data = *base_vector + ab->pci.msi.ep_base_data;271272ath11k_dbg(ab, ATH11K_DBG_PCI,273"msi assignment %s num_vectors %d user_base_data %u base_vector %u\n",274user_name, *num_vectors, *user_base_data,275*base_vector);276277return 0;278}279}280281ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);282283return -EINVAL;284}285EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment);286287void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)288{289u32 i, msi_data_idx;290291for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {292if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)293continue;294295if (ce_id == i)296break;297298msi_data_idx++;299}300*msi_idx = msi_data_idx;301}302EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx);303304static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)305{306int i, j;307308for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {309struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];310311for (j = 0; j < irq_grp->num_irq; j++)312free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);313314netif_napi_del(&irq_grp->napi);315}316}317318void ath11k_pcic_free_irq(struct ath11k_base *ab)319{320int i, irq_idx;321322for (i = 0; i < ab->hw_params.ce_count; i++) {323if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)324continue;325irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;326free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);327}328329ath11k_pcic_free_ext_irq(ab);330}331EXPORT_SYMBOL(ath11k_pcic_free_irq);332333static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)334{335u32 irq_idx;336337/* In case of one MSI vector, we handle irq enable/disable in a338* uniform way since we only have one irq339*/340if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))341return;342343irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;344enable_irq(ab->irq_num[irq_idx]);345}346347static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)348{349u32 irq_idx;350351/* In case of one MSI vector, we handle irq enable/disable in a352* uniform way since we only have one irq353*/354if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))355return;356357irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;358disable_irq_nosync(ab->irq_num[irq_idx]);359}360361static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)362{363int i;364365clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);366367for (i = 0; i < ab->hw_params.ce_count; i++) {368if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)369continue;370ath11k_pcic_ce_irq_disable(ab, i);371}372}373374static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)375{376int i;377int irq_idx;378379for (i = 0; i < ab->hw_params.ce_count; i++) {380if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)381continue;382383irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;384synchronize_irq(ab->irq_num[irq_idx]);385}386}387388static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)389{390struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);391int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;392393ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);394395enable_irq(ce_pipe->ab->irq_num[irq_idx]);396}397398static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)399{400struct ath11k_ce_pipe *ce_pipe = arg;401struct ath11k_base *ab = ce_pipe->ab;402int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;403404if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))405return IRQ_HANDLED;406407/* last interrupt received for this CE */408ce_pipe->timestamp = jiffies;409410disable_irq_nosync(ab->irq_num[irq_idx]);411412tasklet_schedule(&ce_pipe->intr_tq);413414return IRQ_HANDLED;415}416417static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)418{419struct ath11k_base *ab = irq_grp->ab;420int i;421422/* In case of one MSI vector, we handle irq enable/disable423* in a uniform way since we only have one irq424*/425if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))426return;427428for (i = 0; i < irq_grp->num_irq; i++)429disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);430}431432static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *sc)433{434int i;435436clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags);437438for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {439struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i];440441ath11k_pcic_ext_grp_disable(irq_grp);442443if (irq_grp->napi_enabled) {444napi_synchronize(&irq_grp->napi);445napi_disable(&irq_grp->napi);446irq_grp->napi_enabled = false;447}448}449}450451static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)452{453struct ath11k_base *ab = irq_grp->ab;454int i;455456/* In case of one MSI vector, we handle irq enable/disable in a457* uniform way since we only have one irq458*/459if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))460return;461462for (i = 0; i < irq_grp->num_irq; i++)463enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);464}465466void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)467{468int i;469470set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);471472for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {473struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];474475if (!irq_grp->napi_enabled) {476napi_enable(&irq_grp->napi);477irq_grp->napi_enabled = true;478}479ath11k_pcic_ext_grp_enable(irq_grp);480}481}482EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);483484static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)485{486int i, j, irq_idx;487488for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {489struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];490491for (j = 0; j < irq_grp->num_irq; j++) {492irq_idx = irq_grp->irqs[j];493synchronize_irq(ab->irq_num[irq_idx]);494}495}496}497498void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)499{500__ath11k_pcic_ext_irq_disable(ab);501ath11k_pcic_sync_ext_irqs(ab);502}503EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable);504505static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)506{507struct ath11k_ext_irq_grp *irq_grp = container_of(napi,508struct ath11k_ext_irq_grp,509napi);510struct ath11k_base *ab = irq_grp->ab;511int work_done;512int i;513514work_done = ath11k_dp_service_srng(ab, irq_grp, budget);515if (work_done < budget) {516napi_complete_done(napi, work_done);517for (i = 0; i < irq_grp->num_irq; i++)518enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);519}520521if (work_done > budget)522work_done = budget;523524return work_done;525}526527static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)528{529struct ath11k_ext_irq_grp *irq_grp = arg;530struct ath11k_base *ab = irq_grp->ab;531int i;532533if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))534return IRQ_HANDLED;535536ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq %d\n", irq);537538/* last interrupt received for this group */539irq_grp->timestamp = jiffies;540541for (i = 0; i < irq_grp->num_irq; i++)542disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);543544napi_schedule(&irq_grp->napi);545546return IRQ_HANDLED;547}548549static int550ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)551{552return ab->pci.ops->get_msi_irq(ab, vector);553}554555static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)556{557int i, j, ret, num_vectors = 0;558u32 user_base_data = 0, base_vector = 0;559unsigned long irq_flags;560561ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,562&user_base_data,563&base_vector);564if (ret < 0)565return ret;566567irq_flags = IRQF_SHARED;568if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))569irq_flags |= IRQF_NOBALANCING;570571for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {572struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];573u32 num_irq = 0;574575irq_grp->ab = ab;576irq_grp->grp_id = i;577init_dummy_netdev(&irq_grp->napi_ndev);578netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,579ath11k_pcic_ext_grp_napi_poll);580581if (ab->hw_params.ring_mask->tx[i] ||582ab->hw_params.ring_mask->rx[i] ||583ab->hw_params.ring_mask->rx_err[i] ||584ab->hw_params.ring_mask->rx_wbm_rel[i] ||585ab->hw_params.ring_mask->reo_status[i] ||586ab->hw_params.ring_mask->rxdma2host[i] ||587ab->hw_params.ring_mask->host2rxdma[i] ||588ab->hw_params.ring_mask->rx_mon_status[i]) {589num_irq = 1;590}591592irq_grp->num_irq = num_irq;593irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;594595for (j = 0; j < irq_grp->num_irq; j++) {596int irq_idx = irq_grp->irqs[j];597int vector = (i % num_vectors) + base_vector;598int irq = ath11k_pcic_get_msi_irq(ab, vector);599600if (irq < 0)601return irq;602603ab->irq_num[irq_idx] = irq;604605ath11k_dbg(ab, ATH11K_DBG_PCI,606"irq %d group %d\n", irq, i);607608irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);609ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,610irq_flags, "DP_EXT_IRQ", irq_grp);611if (ret) {612ath11k_err(ab, "failed request irq %d: %d\n",613vector, ret);614return ret;615}616}617ath11k_pcic_ext_grp_disable(irq_grp);618}619620return 0;621}622623int ath11k_pcic_config_irq(struct ath11k_base *ab)624{625struct ath11k_ce_pipe *ce_pipe;626u32 msi_data_start;627u32 msi_data_count, msi_data_idx;628u32 msi_irq_start;629unsigned int msi_data;630int irq, i, ret, irq_idx;631unsigned long irq_flags;632633ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,634&msi_data_start, &msi_irq_start);635if (ret)636return ret;637638irq_flags = IRQF_SHARED;639if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))640irq_flags |= IRQF_NOBALANCING;641642/* Configure CE irqs */643for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {644if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)645continue;646647msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;648irq = ath11k_pcic_get_msi_irq(ab, msi_data);649if (irq < 0)650return irq;651652ce_pipe = &ab->ce.ce_pipe[i];653654irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;655656tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);657658ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,659irq_flags, irq_name[irq_idx], ce_pipe);660if (ret) {661ath11k_err(ab, "failed to request irq %d: %d\n",662irq_idx, ret);663return ret;664}665666ab->irq_num[irq_idx] = irq;667msi_data_idx++;668669ath11k_pcic_ce_irq_disable(ab, i);670}671672ret = ath11k_pcic_ext_irq_config(ab);673if (ret)674return ret;675676return 0;677}678EXPORT_SYMBOL(ath11k_pcic_config_irq);679680void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)681{682int i;683684set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);685686for (i = 0; i < ab->hw_params.ce_count; i++) {687if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)688continue;689ath11k_pcic_ce_irq_enable(ab, i);690}691}692EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable);693694static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)695{696int i;697698for (i = 0; i < ab->hw_params.ce_count; i++) {699struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];700701if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)702continue;703704tasklet_kill(&ce_pipe->intr_tq);705}706}707708void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)709{710ath11k_pcic_ce_irqs_disable(ab);711ath11k_pcic_sync_ce_irqs(ab);712ath11k_pcic_kill_tasklets(ab);713}714EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync);715716void ath11k_pcic_stop(struct ath11k_base *ab)717{718ath11k_pcic_ce_irq_disable_sync(ab);719ath11k_ce_cleanup_pipes(ab);720}721EXPORT_SYMBOL(ath11k_pcic_stop);722723int ath11k_pcic_start(struct ath11k_base *ab)724{725set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);726727ath11k_pcic_ce_irqs_enable(ab);728ath11k_ce_rx_post_buf(ab);729730return 0;731}732EXPORT_SYMBOL(ath11k_pcic_start);733734int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,735u8 *ul_pipe, u8 *dl_pipe)736{737const struct service_to_pipe *entry;738bool ul_set = false, dl_set = false;739int i;740741for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {742entry = &ab->hw_params.svc_to_ce_map[i];743744if (__le32_to_cpu(entry->service_id) != service_id)745continue;746747switch (__le32_to_cpu(entry->pipedir)) {748case PIPEDIR_NONE:749break;750case PIPEDIR_IN:751WARN_ON(dl_set);752*dl_pipe = __le32_to_cpu(entry->pipenum);753dl_set = true;754break;755case PIPEDIR_OUT:756WARN_ON(ul_set);757*ul_pipe = __le32_to_cpu(entry->pipenum);758ul_set = true;759break;760case PIPEDIR_INOUT:761WARN_ON(dl_set);762WARN_ON(ul_set);763*dl_pipe = __le32_to_cpu(entry->pipenum);764*ul_pipe = __le32_to_cpu(entry->pipenum);765dl_set = true;766ul_set = true;767break;768}769}770771if (WARN_ON(!ul_set || !dl_set))772return -ENOENT;773774return 0;775}776EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe);777778int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,779const struct ath11k_pci_ops *pci_ops)780{781if (!pci_ops)782return 0;783784/* Return error if mandatory pci_ops callbacks are missing */785if (!pci_ops->get_msi_irq || !pci_ops->window_write32 ||786!pci_ops->window_read32)787return -EINVAL;788789ab->pci.ops = pci_ops;790return 0;791}792EXPORT_SYMBOL(ath11k_pcic_register_pci_ops);793794void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab)795{796int i;797798for (i = 0; i < ab->hw_params.ce_count; i++) {799if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||800i == ATH11K_PCI_CE_WAKE_IRQ)801continue;802ath11k_pcic_ce_irq_enable(ab, i);803}804}805EXPORT_SYMBOL(ath11k_pci_enable_ce_irqs_except_wake_irq);806807void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab)808{809int i;810int irq_idx;811struct ath11k_ce_pipe *ce_pipe;812813for (i = 0; i < ab->hw_params.ce_count; i++) {814ce_pipe = &ab->ce.ce_pipe[i];815irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;816817if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||818i == ATH11K_PCI_CE_WAKE_IRQ)819continue;820821disable_irq_nosync(ab->irq_num[irq_idx]);822synchronize_irq(ab->irq_num[irq_idx]);823tasklet_kill(&ce_pipe->intr_tq);824}825}826EXPORT_SYMBOL(ath11k_pci_disable_ce_irqs_except_wake_irq);827828829