Path: blob/main/sys/contrib/dev/athk/ath12k/ahb.c
178701 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.3* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.4*/56#include <linux/dma-mapping.h>7#include <linux/firmware/qcom/qcom_scm.h>8#include <linux/of.h>9#include <linux/of_device.h>10#include <linux/platform_device.h>11#include <linux/remoteproc.h>12#include <linux/soc/qcom/mdt_loader.h>13#include <linux/soc/qcom/smem_state.h>14#include "ahb.h"15#include "debug.h"16#include "hif.h"1718static const struct of_device_id ath12k_ahb_of_match[] = {19{ .compatible = "qcom,ipq5332-wifi",20.data = (void *)ATH12K_HW_IPQ5332_HW10,21},22{ }23};2425MODULE_DEVICE_TABLE(of, ath12k_ahb_of_match);2627#define ATH12K_IRQ_CE0_OFFSET 428#define ATH12K_MAX_UPDS 129#define ATH12K_UPD_IRQ_WRD_LEN 1830static const char ath12k_userpd_irq[][9] = {"spawn",31"ready",32"stop-ack"};3334static const char *irq_name[ATH12K_IRQ_NUM_MAX] = {35"misc-pulse1",36"misc-latch",37"sw-exception",38"watchdog",39"ce0",40"ce1",41"ce2",42"ce3",43"ce4",44"ce5",45"ce6",46"ce7",47"ce8",48"ce9",49"ce10",50"ce11",51"host2wbm-desc-feed",52"host2reo-re-injection",53"host2reo-command",54"host2rxdma-monitor-ring3",55"host2rxdma-monitor-ring2",56"host2rxdma-monitor-ring1",57"reo2ost-exception",58"wbm2host-rx-release",59"reo2host-status",60"reo2host-destination-ring4",61"reo2host-destination-ring3",62"reo2host-destination-ring2",63"reo2host-destination-ring1",64"rxdma2host-monitor-destination-mac3",65"rxdma2host-monitor-destination-mac2",66"rxdma2host-monitor-destination-mac1",67"ppdu-end-interrupts-mac3",68"ppdu-end-interrupts-mac2",69"ppdu-end-interrupts-mac1",70"rxdma2host-monitor-status-ring-mac3",71"rxdma2host-monitor-status-ring-mac2",72"rxdma2host-monitor-status-ring-mac1",73"host2rxdma-host-buf-ring-mac3",74"host2rxdma-host-buf-ring-mac2",75"host2rxdma-host-buf-ring-mac1",76"rxdma2host-destination-ring-mac3",77"rxdma2host-destination-ring-mac2",78"rxdma2host-destination-ring-mac1",79"host2tcl-input-ring4",80"host2tcl-input-ring3",81"host2tcl-input-ring2",82"host2tcl-input-ring1",83"wbm2host-tx-completions-ring4",84"wbm2host-tx-completions-ring3",85"wbm2host-tx-completions-ring2",86"wbm2host-tx-completions-ring1",87"tcl2host-status-ring",88};8990enum ext_irq_num {91host2wbm_desc_feed = 16,92host2reo_re_injection,93host2reo_command,94host2rxdma_monitor_ring3,95host2rxdma_monitor_ring2,96host2rxdma_monitor_ring1,97reo2host_exception,98wbm2host_rx_release,99reo2host_status,100reo2host_destination_ring4,101reo2host_destination_ring3,102reo2host_destination_ring2,103reo2host_destination_ring1,104rxdma2host_monitor_destination_mac3,105rxdma2host_monitor_destination_mac2,106rxdma2host_monitor_destination_mac1,107ppdu_end_interrupts_mac3,108ppdu_end_interrupts_mac2,109ppdu_end_interrupts_mac1,110rxdma2host_monitor_status_ring_mac3,111rxdma2host_monitor_status_ring_mac2,112rxdma2host_monitor_status_ring_mac1,113host2rxdma_host_buf_ring_mac3,114host2rxdma_host_buf_ring_mac2,115host2rxdma_host_buf_ring_mac1,116rxdma2host_destination_ring_mac3,117rxdma2host_destination_ring_mac2,118rxdma2host_destination_ring_mac1,119host2tcl_input_ring4,120host2tcl_input_ring3,121host2tcl_input_ring2,122host2tcl_input_ring1,123wbm2host_tx_completions_ring4,124wbm2host_tx_completions_ring3,125wbm2host_tx_completions_ring2,126wbm2host_tx_completions_ring1,127tcl2host_status_ring,128};129130static u32 ath12k_ahb_read32(struct ath12k_base *ab, u32 offset)131{132if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET)133return ioread32(ab->mem_ce + offset);134return ioread32(ab->mem + offset);135}136137static void ath12k_ahb_write32(struct ath12k_base *ab, u32 offset,138u32 value)139{140if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET)141iowrite32(value, ab->mem_ce + offset);142else143iowrite32(value, ab->mem + offset);144}145146static void ath12k_ahb_cancel_workqueue(struct ath12k_base *ab)147{148int i;149150for (i = 0; i < ab->hw_params->ce_count; i++) {151struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];152153if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)154continue;155156cancel_work_sync(&ce_pipe->intr_wq);157}158}159160static void ath12k_ahb_ext_grp_disable(struct ath12k_ext_irq_grp *irq_grp)161{162int i;163164for (i = 0; i < irq_grp->num_irq; i++)165disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);166}167168static void __ath12k_ahb_ext_irq_disable(struct ath12k_base *ab)169{170int i;171172for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {173struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];174175ath12k_ahb_ext_grp_disable(irq_grp);176if (irq_grp->napi_enabled) {177napi_synchronize(&irq_grp->napi);178napi_disable(&irq_grp->napi);179irq_grp->napi_enabled = false;180}181}182}183184static void ath12k_ahb_ext_grp_enable(struct ath12k_ext_irq_grp *irq_grp)185{186int i;187188for (i = 0; i < irq_grp->num_irq; i++)189enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);190}191192static void ath12k_ahb_setbit32(struct ath12k_base *ab, u8 bit, u32 offset)193{194u32 val;195196val = ath12k_ahb_read32(ab, offset);197ath12k_ahb_write32(ab, offset, val | BIT(bit));198}199200static void ath12k_ahb_clearbit32(struct ath12k_base *ab, u8 bit, u32 offset)201{202u32 val;203204val = ath12k_ahb_read32(ab, offset);205ath12k_ahb_write32(ab, offset, val & ~BIT(bit));206}207208static void ath12k_ahb_ce_irq_enable(struct ath12k_base *ab, u16 ce_id)209{210const struct ce_attr *ce_attr;211const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr;212u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;213214ie1_reg_addr = ce_ie_addr->ie1_reg_addr;215ie2_reg_addr = ce_ie_addr->ie2_reg_addr;216ie3_reg_addr = ce_ie_addr->ie3_reg_addr;217218ce_attr = &ab->hw_params->host_ce_config[ce_id];219if (ce_attr->src_nentries)220ath12k_ahb_setbit32(ab, ce_id, ie1_reg_addr);221222if (ce_attr->dest_nentries) {223ath12k_ahb_setbit32(ab, ce_id, ie2_reg_addr);224ath12k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT,225ie3_reg_addr);226}227}228229static void ath12k_ahb_ce_irq_disable(struct ath12k_base *ab, u16 ce_id)230{231const struct ce_attr *ce_attr;232const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr;233u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;234235ie1_reg_addr = ce_ie_addr->ie1_reg_addr;236ie2_reg_addr = ce_ie_addr->ie2_reg_addr;237ie3_reg_addr = ce_ie_addr->ie3_reg_addr;238239ce_attr = &ab->hw_params->host_ce_config[ce_id];240if (ce_attr->src_nentries)241ath12k_ahb_clearbit32(ab, ce_id, ie1_reg_addr);242243if (ce_attr->dest_nentries) {244ath12k_ahb_clearbit32(ab, ce_id, ie2_reg_addr);245ath12k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT,246ie3_reg_addr);247}248}249250static void ath12k_ahb_sync_ce_irqs(struct ath12k_base *ab)251{252int i;253int irq_idx;254255for (i = 0; i < ab->hw_params->ce_count; i++) {256if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)257continue;258259irq_idx = ATH12K_IRQ_CE0_OFFSET + i;260synchronize_irq(ab->irq_num[irq_idx]);261}262}263264static void ath12k_ahb_sync_ext_irqs(struct ath12k_base *ab)265{266int i, j;267int irq_idx;268269for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {270struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];271272for (j = 0; j < irq_grp->num_irq; j++) {273irq_idx = irq_grp->irqs[j];274synchronize_irq(ab->irq_num[irq_idx]);275}276}277}278279static void ath12k_ahb_ce_irqs_enable(struct ath12k_base *ab)280{281int i;282283for (i = 0; i < ab->hw_params->ce_count; i++) {284if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)285continue;286ath12k_ahb_ce_irq_enable(ab, i);287}288}289290static void ath12k_ahb_ce_irqs_disable(struct ath12k_base *ab)291{292int i;293294for (i = 0; i < ab->hw_params->ce_count; i++) {295if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)296continue;297ath12k_ahb_ce_irq_disable(ab, i);298}299}300301static int ath12k_ahb_start(struct ath12k_base *ab)302{303ath12k_ahb_ce_irqs_enable(ab);304ath12k_ce_rx_post_buf(ab);305306return 0;307}308309static void ath12k_ahb_ext_irq_enable(struct ath12k_base *ab)310{311struct ath12k_ext_irq_grp *irq_grp;312int i;313314for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {315irq_grp = &ab->ext_irq_grp[i];316if (!irq_grp->napi_enabled) {317napi_enable(&irq_grp->napi);318irq_grp->napi_enabled = true;319}320ath12k_ahb_ext_grp_enable(irq_grp);321}322}323324static void ath12k_ahb_ext_irq_disable(struct ath12k_base *ab)325{326__ath12k_ahb_ext_irq_disable(ab);327ath12k_ahb_sync_ext_irqs(ab);328}329330static void ath12k_ahb_stop(struct ath12k_base *ab)331{332if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))333ath12k_ahb_ce_irqs_disable(ab);334ath12k_ahb_sync_ce_irqs(ab);335ath12k_ahb_cancel_workqueue(ab);336timer_delete_sync(&ab->rx_replenish_retry);337ath12k_ce_cleanup_pipes(ab);338}339340static int ath12k_ahb_power_up(struct ath12k_base *ab)341{342struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);343char fw_name[ATH12K_USERPD_FW_NAME_LEN];344char fw2_name[ATH12K_USERPD_FW_NAME_LEN];345struct device *dev = ab->dev;346const struct firmware *fw, *fw2;347struct reserved_mem *rmem = NULL;348unsigned long time_left;349phys_addr_t mem_phys;350void *mem_region;351size_t mem_size;352u32 pasid;353int ret;354355rmem = ath12k_core_get_reserved_mem(ab, 0);356if (!rmem)357return -ENODEV;358359mem_phys = rmem->base;360mem_size = rmem->size;361mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC);362if (IS_ERR(mem_region)) {363ath12k_err(ab, "unable to map memory region: %pa+%pa\n",364&rmem->base, &rmem->size);365return PTR_ERR(mem_region);366}367368snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR,369ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id,370ATH12K_AHB_FW_SUFFIX);371372ret = request_firmware(&fw, fw_name, dev);373if (ret < 0) {374ath12k_err(ab, "request_firmware failed\n");375return ret;376}377378ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw_name,379fw->size);380381if (!fw->size) {382ath12k_err(ab, "Invalid firmware size\n");383ret = -EINVAL;384goto err_fw;385}386387pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) |388ATH12K_AHB_UPD_SWID;389390/* Load FW image to a reserved memory location */391ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size,392&mem_phys);393if (ret) {394ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);395goto err_fw;396}397398snprintf(fw2_name, sizeof(fw2_name), "%s/%s/%s", ATH12K_FW_DIR,399ab->hw_params->fw.dir, ATH12K_AHB_FW2);400401ret = request_firmware(&fw2, fw2_name, dev);402if (ret < 0) {403ath12k_err(ab, "request_firmware failed\n");404goto err_fw;405}406407ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw2_name,408fw2->size);409410if (!fw2->size) {411ath12k_err(ab, "Invalid firmware size\n");412ret = -EINVAL;413goto err_fw2;414}415416ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, mem_region, mem_phys,417mem_size, &mem_phys);418if (ret) {419ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);420goto err_fw2;421}422423/* Authenticate FW image using peripheral ID */424ret = qcom_scm_pas_auth_and_reset(pasid);425if (ret) {426ath12k_err(ab, "failed to boot the remote processor %d\n", ret);427goto err_fw2;428}429430/* Instruct Q6 to spawn userPD thread */431ret = qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit),432BIT(ab_ahb->spawn_bit));433if (ret) {434ath12k_err(ab, "Failed to update spawn state %d\n", ret);435goto err_fw2;436}437438time_left = wait_for_completion_timeout(&ab_ahb->userpd_spawned,439ATH12K_USERPD_SPAWN_TIMEOUT);440if (!time_left) {441ath12k_err(ab, "UserPD spawn wait timed out\n");442ret = -ETIMEDOUT;443goto err_fw2;444}445446time_left = wait_for_completion_timeout(&ab_ahb->userpd_ready,447ATH12K_USERPD_READY_TIMEOUT);448if (!time_left) {449ath12k_err(ab, "UserPD ready wait timed out\n");450ret = -ETIMEDOUT;451goto err_fw2;452}453454qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), 0);455456ath12k_dbg(ab, ATH12K_DBG_AHB, "UserPD%d is now UP\n", ab_ahb->userpd_id);457458err_fw2:459release_firmware(fw2);460err_fw:461release_firmware(fw);462return ret;463}464465static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend)466{467struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);468unsigned long time_left;469u32 pasid;470int ret;471472qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit),473BIT(ab_ahb->stop_bit));474475time_left = wait_for_completion_timeout(&ab_ahb->userpd_stopped,476ATH12K_USERPD_STOP_TIMEOUT);477if (!time_left) {478ath12k_err(ab, "UserPD stop wait timed out\n");479return;480}481482qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0);483484pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) |485ATH12K_AHB_UPD_SWID;486/* Release the firmware */487ret = qcom_scm_pas_shutdown(pasid);488if (ret)489ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n",490ab_ahb->userpd_id, ret);491}492493static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab)494{495struct ath12k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;496497cfg->tgt_ce_len = ab->hw_params->target_ce_count;498cfg->tgt_ce = ab->hw_params->target_ce_config;499cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len;500cfg->svc_to_ce_map = ab->hw_params->svc_to_ce_map;501ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id;502}503504static void ath12k_ahb_ce_workqueue(struct work_struct *work)505{506struct ath12k_ce_pipe *ce_pipe = from_work(ce_pipe, work, intr_wq);507508ath12k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);509510ath12k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num);511}512513static irqreturn_t ath12k_ahb_ce_interrupt_handler(int irq, void *arg)514{515struct ath12k_ce_pipe *ce_pipe = arg;516517/* last interrupt received for this CE */518ce_pipe->timestamp = jiffies;519520ath12k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num);521522queue_work(system_bh_wq, &ce_pipe->intr_wq);523524return IRQ_HANDLED;525}526527static int ath12k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget)528{529struct ath12k_ext_irq_grp *irq_grp = container_of(napi,530struct ath12k_ext_irq_grp,531napi);532struct ath12k_base *ab = irq_grp->ab;533int work_done;534535work_done = ath12k_dp_service_srng(ab, irq_grp, budget);536if (work_done < budget) {537napi_complete_done(napi, work_done);538ath12k_ahb_ext_grp_enable(irq_grp);539}540541if (work_done > budget)542work_done = budget;543544return work_done;545}546547static irqreturn_t ath12k_ahb_ext_interrupt_handler(int irq, void *arg)548{549struct ath12k_ext_irq_grp *irq_grp = arg;550551/* last interrupt received for this group */552irq_grp->timestamp = jiffies;553554ath12k_ahb_ext_grp_disable(irq_grp);555556napi_schedule(&irq_grp->napi);557558return IRQ_HANDLED;559}560561static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab)562{563const struct ath12k_hw_ring_mask *ring_mask;564struct ath12k_ext_irq_grp *irq_grp;565const struct hal_ops *hal_ops;566int i, j, irq, irq_idx, ret;567u32 num_irq;568569ring_mask = ab->hw_params->ring_mask;570hal_ops = ab->hw_params->hal_ops;571for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) {572irq_grp = &ab->ext_irq_grp[i];573num_irq = 0;574575irq_grp->ab = ab;576irq_grp->grp_id = i;577578irq_grp->napi_ndev = alloc_netdev_dummy(0);579if (!irq_grp->napi_ndev)580return -ENOMEM;581582netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,583ath12k_ahb_ext_grp_napi_poll);584585for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) {586/* For TX ring, ensure that the ring mask and the587* tcl_to_wbm_rbm_map point to the same ring number.588*/589if (ring_mask->tx[i] &590BIT(hal_ops->tcl_to_wbm_rbm_map[j].wbm_ring_num)) {591irq_grp->irqs[num_irq++] =592wbm2host_tx_completions_ring1 - j;593}594595if (ring_mask->rx[i] & BIT(j)) {596irq_grp->irqs[num_irq++] =597reo2host_destination_ring1 - j;598}599600if (ring_mask->rx_err[i] & BIT(j))601irq_grp->irqs[num_irq++] = reo2host_exception;602603if (ring_mask->rx_wbm_rel[i] & BIT(j))604irq_grp->irqs[num_irq++] = wbm2host_rx_release;605606if (ring_mask->reo_status[i] & BIT(j))607irq_grp->irqs[num_irq++] = reo2host_status;608609if (ring_mask->rx_mon_dest[i] & BIT(j))610irq_grp->irqs[num_irq++] =611rxdma2host_monitor_destination_mac1;612}613614irq_grp->num_irq = num_irq;615616for (j = 0; j < irq_grp->num_irq; j++) {617irq_idx = irq_grp->irqs[j];618619irq = platform_get_irq_byname(ab->pdev,620irq_name[irq_idx]);621ab->irq_num[irq_idx] = irq;622irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY);623ret = devm_request_irq(ab->dev, irq,624ath12k_ahb_ext_interrupt_handler,625IRQF_TRIGGER_RISING,626irq_name[irq_idx], irq_grp);627if (ret)628ath12k_warn(ab, "failed request_irq for %d\n", irq);629}630}631632return 0;633}634635static int ath12k_ahb_config_irq(struct ath12k_base *ab)636{637int irq, irq_idx, i;638int ret;639640/* Configure CE irqs */641for (i = 0; i < ab->hw_params->ce_count; i++) {642struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];643644if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)645continue;646647irq_idx = ATH12K_IRQ_CE0_OFFSET + i;648649INIT_WORK(&ce_pipe->intr_wq, ath12k_ahb_ce_workqueue);650irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]);651ret = devm_request_irq(ab->dev, irq, ath12k_ahb_ce_interrupt_handler,652IRQF_TRIGGER_RISING, irq_name[irq_idx],653ce_pipe);654if (ret)655return ret;656657ab->irq_num[irq_idx] = irq;658}659660/* Configure external interrupts */661ret = ath12k_ahb_config_ext_irq(ab);662663return ret;664}665666static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id,667u8 *ul_pipe, u8 *dl_pipe)668{669const struct service_to_pipe *entry;670bool ul_set = false, dl_set = false;671u32 pipedir;672int i;673674for (i = 0; i < ab->hw_params->svc_to_ce_map_len; i++) {675entry = &ab->hw_params->svc_to_ce_map[i];676677if (__le32_to_cpu(entry->service_id) != service_id)678continue;679680pipedir = __le32_to_cpu(entry->pipedir);681if (pipedir == PIPEDIR_IN || pipedir == PIPEDIR_INOUT) {682WARN_ON(dl_set);683*dl_pipe = __le32_to_cpu(entry->pipenum);684dl_set = true;685}686687if (pipedir == PIPEDIR_OUT || pipedir == PIPEDIR_INOUT) {688WARN_ON(ul_set);689*ul_pipe = __le32_to_cpu(entry->pipenum);690ul_set = true;691}692}693694if (WARN_ON(!ul_set || !dl_set))695return -ENOENT;696697return 0;698}699700static const struct ath12k_hif_ops ath12k_ahb_hif_ops_ipq5332 = {701.start = ath12k_ahb_start,702.stop = ath12k_ahb_stop,703.read32 = ath12k_ahb_read32,704.write32 = ath12k_ahb_write32,705.irq_enable = ath12k_ahb_ext_irq_enable,706.irq_disable = ath12k_ahb_ext_irq_disable,707.map_service_to_pipe = ath12k_ahb_map_service_to_pipe,708.power_up = ath12k_ahb_power_up,709.power_down = ath12k_ahb_power_down,710};711712static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data)713{714struct ath12k_base *ab = data;715struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);716717if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_SPAWN_IRQ]) {718complete(&ab_ahb->userpd_spawned);719} else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_READY_IRQ]) {720complete(&ab_ahb->userpd_ready);721} else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_STOP_ACK_IRQ]) {722complete(&ab_ahb->userpd_stopped);723} else {724ath12k_err(ab, "Invalid userpd interrupt\n");725return IRQ_NONE;726}727728return IRQ_HANDLED;729}730731static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab)732{733struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);734int i, ret;735char *upd_irq_name;736737for (i = 0; i < ATH12K_USERPD_MAX_IRQ; i++) {738ab_ahb->userpd_irq_num[i] = platform_get_irq_byname(ab->pdev,739ath12k_userpd_irq[i]);740if (ab_ahb->userpd_irq_num[i] < 0)741return ab_ahb->userpd_irq_num[i];742743upd_irq_name = devm_kzalloc(&ab->pdev->dev, ATH12K_UPD_IRQ_WRD_LEN,744GFP_KERNEL);745if (!upd_irq_name)746return -ENOMEM;747748scnprintf(upd_irq_name, ATH12K_UPD_IRQ_WRD_LEN, "UserPD%u-%s",749ab_ahb->userpd_id, ath12k_userpd_irq[i]);750ret = devm_request_threaded_irq(&ab->pdev->dev, ab_ahb->userpd_irq_num[i],751NULL, ath12k_userpd_irq_handler,752IRQF_TRIGGER_RISING | IRQF_ONESHOT,753upd_irq_name, ab);754if (ret)755return dev_err_probe(&ab->pdev->dev, ret,756"Request %s irq failed: %d\n",757ath12k_userpd_irq[i], ret);758}759760ab_ahb->spawn_state = devm_qcom_smem_state_get(&ab->pdev->dev, "spawn",761&ab_ahb->spawn_bit);762if (IS_ERR(ab_ahb->spawn_state))763return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->spawn_state),764"Failed to acquire spawn state\n");765766ab_ahb->stop_state = devm_qcom_smem_state_get(&ab->pdev->dev, "stop",767&ab_ahb->stop_bit);768if (IS_ERR(ab_ahb->stop_state))769return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->stop_state),770"Failed to acquire stop state\n");771772init_completion(&ab_ahb->userpd_spawned);773init_completion(&ab_ahb->userpd_ready);774init_completion(&ab_ahb->userpd_stopped);775return 0;776}777778static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb,779const unsigned long event, void *data)780{781struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb);782struct ath12k_base *ab = ab_ahb->ab;783784if (event == ATH12K_RPROC_AFTER_POWERUP) {785ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n");786complete(&ab_ahb->rootpd_ready);787}788789return 0;790}791792static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab)793{794struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);795796ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier;797init_completion(&ab_ahb->rootpd_ready);798799ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name,800&ab_ahb->root_pd_nb);801if (IS_ERR(ab_ahb->root_pd_notifier))802return PTR_ERR(ab_ahb->root_pd_notifier);803804return 0;805}806807static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab)808{809struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);810811if (!ab_ahb->root_pd_notifier) {812ath12k_err(ab, "Rproc notifier not registered\n");813return;814}815816qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier,817&ab_ahb->root_pd_nb);818ab_ahb->root_pd_notifier = NULL;819}820821static int ath12k_ahb_get_rproc(struct ath12k_base *ab)822{823struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);824struct device *dev = ab->dev;825struct device_node *np;826struct rproc *prproc;827828np = of_parse_phandle(dev->of_node, "qcom,rproc", 0);829if (!np) {830ath12k_err(ab, "failed to get q6_rproc handle\n");831return -ENOENT;832}833834prproc = rproc_get_by_phandle(np->phandle);835of_node_put(np);836if (!prproc)837return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER,838"failed to get rproc\n");839840ab_ahb->tgt_rproc = prproc;841842return 0;843}844845static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)846{847struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);848unsigned long time_left;849int ret;850851ret = rproc_boot(ab_ahb->tgt_rproc);852if (ret < 0) {853ath12k_err(ab, "RootPD boot failed\n");854return ret;855}856857time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready,858ATH12K_ROOTPD_READY_TIMEOUT);859if (!time_left) {860ath12k_err(ab, "RootPD ready wait timed out\n");861return -ETIMEDOUT;862}863864return 0;865}866867static int ath12k_ahb_configure_rproc(struct ath12k_base *ab)868{869struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);870int ret;871872ret = ath12k_ahb_get_rproc(ab);873if (ret < 0)874return ret;875876ret = ath12k_ahb_register_rproc_notifier(ab);877if (ret < 0) {878ret = dev_err_probe(&ab->pdev->dev, ret,879"failed to register rproc notifier\n");880goto err_put_rproc;881}882883if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) {884ret = ath12k_ahb_boot_root_pd(ab);885if (ret < 0) {886ath12k_err(ab, "failed to boot the remote processor Q6\n");887goto err_unreg_notifier;888}889}890891return ath12k_ahb_config_rproc_irq(ab);892893err_unreg_notifier:894ath12k_ahb_unregister_rproc_notifier(ab);895896err_put_rproc:897rproc_put(ab_ahb->tgt_rproc);898return ret;899}900901static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab)902{903struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);904905ath12k_ahb_unregister_rproc_notifier(ab);906rproc_put(ab_ahb->tgt_rproc);907}908909static int ath12k_ahb_resource_init(struct ath12k_base *ab)910{911struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);912struct platform_device *pdev = ab->pdev;913struct resource *mem_res;914int ret;915916ab->mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);917if (IS_ERR(ab->mem)) {918ret = dev_err_probe(&pdev->dev, PTR_ERR(ab->mem), "ioremap error\n");919goto out;920}921922ab->mem_len = resource_size(mem_res);923924if (ab->hw_params->ce_remap) {925const struct ce_remap *ce_remap = ab->hw_params->ce_remap;926/* CE register space is moved out of WCSS and the space is not927* contiguous, hence remapping the CE registers to a new space928* for accessing them.929*/930ab->mem_ce = ioremap(ce_remap->base, ce_remap->size);931if (!ab->mem_ce) {932dev_err(&pdev->dev, "ce ioremap error\n");933ret = -ENOMEM;934goto err_mem_unmap;935}936ab->ce_remap = true;937ab->ce_remap_base_addr = HAL_IPQ5332_CE_WFSS_REG_BASE;938}939940ab_ahb->xo_clk = devm_clk_get(ab->dev, "xo");941if (IS_ERR(ab_ahb->xo_clk)) {942ret = dev_err_probe(&pdev->dev, PTR_ERR(ab_ahb->xo_clk),943"failed to get xo clock\n");944goto err_mem_ce_unmap;945}946947ret = clk_prepare_enable(ab_ahb->xo_clk);948if (ret) {949dev_err(&pdev->dev, "failed to enable gcc_xo_clk: %d\n", ret);950goto err_clock_deinit;951}952953return 0;954955err_clock_deinit:956devm_clk_put(ab->dev, ab_ahb->xo_clk);957958err_mem_ce_unmap:959ab_ahb->xo_clk = NULL;960if (ab->hw_params->ce_remap)961iounmap(ab->mem_ce);962963err_mem_unmap:964ab->mem_ce = NULL;965devm_iounmap(ab->dev, ab->mem);966967out:968ab->mem = NULL;969return ret;970}971972static void ath12k_ahb_resource_deinit(struct ath12k_base *ab)973{974struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);975976if (ab->mem)977devm_iounmap(ab->dev, ab->mem);978979if (ab->mem_ce)980iounmap(ab->mem_ce);981982ab->mem = NULL;983ab->mem_ce = NULL;984985clk_disable_unprepare(ab_ahb->xo_clk);986devm_clk_put(ab->dev, ab_ahb->xo_clk);987ab_ahb->xo_clk = NULL;988}989990static int ath12k_ahb_probe(struct platform_device *pdev)991{992struct ath12k_base *ab;993const struct ath12k_hif_ops *hif_ops;994struct ath12k_ahb *ab_ahb;995enum ath12k_hw_rev hw_rev;996u32 addr, userpd_id;997int ret;998999ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));1000if (ret) {1001dev_err(&pdev->dev, "Failed to set 32-bit coherent dma\n");1002return ret;1003}10041005ab = ath12k_core_alloc(&pdev->dev, sizeof(struct ath12k_ahb),1006ATH12K_BUS_AHB);1007if (!ab)1008return -ENOMEM;10091010hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev);1011switch (hw_rev) {1012case ATH12K_HW_IPQ5332_HW10:1013hif_ops = &ath12k_ahb_hif_ops_ipq5332;1014userpd_id = ATH12K_IPQ5332_USERPD_ID;1015break;1016default:1017ret = -EOPNOTSUPP;1018goto err_core_free;1019}10201021ab->hif.ops = hif_ops;1022ab->pdev = pdev;1023ab->hw_rev = hw_rev;1024ab->target_mem_mode = ATH12K_QMI_MEMORY_MODE_DEFAULT;1025platform_set_drvdata(pdev, ab);1026ab_ahb = ath12k_ab_to_ahb(ab);1027ab_ahb->ab = ab;1028ab_ahb->userpd_id = userpd_id;10291030/* Set fixed_mem_region to true for platforms that support fixed memory1031* reservation from DT. If memory is reserved from DT for FW, ath12k driver1032* need not to allocate memory.1033*/1034if (!of_property_read_u32(ab->dev->of_node, "memory-region", &addr))1035set_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags);10361037ret = ath12k_core_pre_init(ab);1038if (ret)1039goto err_core_free;10401041ret = ath12k_ahb_resource_init(ab);1042if (ret)1043goto err_core_free;10441045ret = ath12k_hal_srng_init(ab);1046if (ret)1047goto err_resource_deinit;10481049ret = ath12k_ce_alloc_pipes(ab);1050if (ret) {1051ath12k_err(ab, "failed to allocate ce pipes: %d\n", ret);1052goto err_hal_srng_deinit;1053}10541055ath12k_ahb_init_qmi_ce_config(ab);10561057ret = ath12k_ahb_configure_rproc(ab);1058if (ret)1059goto err_ce_free;10601061ret = ath12k_ahb_config_irq(ab);1062if (ret) {1063ath12k_err(ab, "failed to configure irq: %d\n", ret);1064goto err_rproc_deconfigure;1065}10661067ret = ath12k_core_init(ab);1068if (ret) {1069ath12k_err(ab, "failed to init core: %d\n", ret);1070goto err_rproc_deconfigure;1071}10721073return 0;10741075err_rproc_deconfigure:1076ath12k_ahb_deconfigure_rproc(ab);10771078err_ce_free:1079ath12k_ce_free_pipes(ab);10801081err_hal_srng_deinit:1082ath12k_hal_srng_deinit(ab);10831084err_resource_deinit:1085ath12k_ahb_resource_deinit(ab);10861087err_core_free:1088ath12k_core_free(ab);1089platform_set_drvdata(pdev, NULL);10901091return ret;1092}10931094static void ath12k_ahb_remove_prepare(struct ath12k_base *ab)1095{1096unsigned long left;10971098if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) {1099left = wait_for_completion_timeout(&ab->driver_recovery,1100ATH12K_AHB_RECOVERY_TIMEOUT);1101if (!left)1102ath12k_warn(ab, "failed to receive recovery response completion\n");1103}11041105set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags);1106cancel_work_sync(&ab->restart_work);1107cancel_work_sync(&ab->qmi.event_work);1108}11091110static void ath12k_ahb_free_resources(struct ath12k_base *ab)1111{1112struct platform_device *pdev = ab->pdev;11131114ath12k_hal_srng_deinit(ab);1115ath12k_ce_free_pipes(ab);1116ath12k_ahb_resource_deinit(ab);1117ath12k_ahb_deconfigure_rproc(ab);1118ath12k_core_free(ab);1119platform_set_drvdata(pdev, NULL);1120}11211122static void ath12k_ahb_remove(struct platform_device *pdev)1123{1124struct ath12k_base *ab = platform_get_drvdata(pdev);11251126if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) {1127ath12k_ahb_power_down(ab, false);1128goto qmi_fail;1129}11301131ath12k_ahb_remove_prepare(ab);1132ath12k_core_hw_group_cleanup(ab->ag);1133qmi_fail:1134ath12k_core_deinit(ab);1135ath12k_ahb_free_resources(ab);1136}11371138static struct platform_driver ath12k_ahb_driver = {1139.driver = {1140.name = "ath12k_ahb",1141.of_match_table = ath12k_ahb_of_match,1142},1143.probe = ath12k_ahb_probe,1144.remove = ath12k_ahb_remove,1145};11461147int ath12k_ahb_init(void)1148{1149return platform_driver_register(&ath12k_ahb_driver);1150}11511152void ath12k_ahb_exit(void)1153{1154platform_driver_unregister(&ath12k_ahb_driver);1155}115611571158