Path: blob/main/sys/contrib/dev/athk/ath11k/mhi.c
107769 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2020 The Linux Foundation. All rights reserved.3* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.4*/56#include <linux/msi.h>7#include <linux/pci.h>8#include <linux/firmware.h>9#if defined(CONFIG_OF)10#include <linux/of.h>11#include <linux/of_address.h>12#endif13#include <linux/ioport.h>14#if defined(__FreeBSD__)15#include <linux/delay.h>16#endif1718#include "core.h"19#include "debug.h"20#include "mhi.h"21#include "pci.h"22#include "pcic.h"2324#define MHI_TIMEOUT_DEFAULT_MS 2000025#define RDDM_DUMP_SIZE 0x42000026#define MHI_CB_INVALID 0xff2728static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {29{30.num = 20,31.name = "IPCR",32.num_elements = 64,33.event_ring = 1,34.dir = DMA_TO_DEVICE,35.ee_mask = 0x4,36.pollcfg = 0,37.doorbell = MHI_DB_BRST_DISABLE,38.lpm_notify = false,39.offload_channel = false,40.doorbell_mode_switch = false,41.auto_queue = false,42},43{44.num = 21,45.name = "IPCR",46.num_elements = 64,47.event_ring = 1,48.dir = DMA_FROM_DEVICE,49.ee_mask = 0x4,50.pollcfg = 0,51.doorbell = MHI_DB_BRST_DISABLE,52.lpm_notify = false,53.offload_channel = false,54.doorbell_mode_switch = false,55.auto_queue = true,56},57};5859static struct mhi_event_config ath11k_mhi_events_qca6390[] = {60{61.num_elements = 32,62.irq_moderation_ms = 0,63.irq = 1,64.mode = MHI_DB_BRST_DISABLE,65.data_type = MHI_ER_CTRL,66.hardware_event = false,67.client_managed = false,68.offload_channel = false,69},70{71.num_elements = 256,72.irq_moderation_ms = 1,73.irq = 2,74.mode = MHI_DB_BRST_DISABLE,75.priority = 1,76.hardware_event = false,77.client_managed = false,78.offload_channel = false,79},80};8182static const struct mhi_controller_config ath11k_mhi_config_qca6390 = {83.max_channels = 128,84.timeout_ms = 2000,85.use_bounce_buf = false,86.buf_len = 8192,87.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qca6390),88.ch_cfg = ath11k_mhi_channels_qca6390,89.num_events = ARRAY_SIZE(ath11k_mhi_events_qca6390),90.event_cfg = ath11k_mhi_events_qca6390,91};9293static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {94{95.num = 20,96.name = "IPCR",97.num_elements = 32,98.event_ring = 1,99.dir = DMA_TO_DEVICE,100.ee_mask = 0x14,101.pollcfg = 0,102.doorbell = MHI_DB_BRST_DISABLE,103.lpm_notify = false,104.offload_channel = false,105.doorbell_mode_switch = false,106.auto_queue = false,107},108{109.num = 21,110.name = "IPCR",111.num_elements = 32,112.event_ring = 1,113.dir = DMA_FROM_DEVICE,114.ee_mask = 0x14,115.pollcfg = 0,116.doorbell = MHI_DB_BRST_DISABLE,117.lpm_notify = false,118.offload_channel = false,119.doorbell_mode_switch = false,120.auto_queue = true,121},122};123124static struct mhi_event_config ath11k_mhi_events_qcn9074[] = {125{126.num_elements = 32,127.irq_moderation_ms = 0,128.irq = 1,129.data_type = MHI_ER_CTRL,130.mode = MHI_DB_BRST_DISABLE,131.hardware_event = false,132.client_managed = false,133.offload_channel = false,134},135{136.num_elements = 256,137.irq_moderation_ms = 1,138.irq = 2,139.mode = MHI_DB_BRST_DISABLE,140.priority = 1,141.hardware_event = false,142.client_managed = false,143.offload_channel = false,144},145};146147static const struct mhi_controller_config ath11k_mhi_config_qcn9074 = {148.max_channels = 30,149.timeout_ms = 10000,150.use_bounce_buf = false,151.buf_len = 0,152.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qcn9074),153.ch_cfg = ath11k_mhi_channels_qcn9074,154.num_events = ARRAY_SIZE(ath11k_mhi_events_qcn9074),155.event_cfg = ath11k_mhi_events_qcn9074,156};157158void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)159{160u32 val;161162val = ath11k_pcic_read32(ab, MHISTATUS);163164ath11k_dbg(ab, ATH11K_DBG_PCI, "mhistatus 0x%x\n", val);165166/* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set167* and thus need to set MHICTRL_RESET to clear SYSERR.168*/169ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK);170171mdelay(10);172}173174static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab)175{176ath11k_pcic_write32(ab, PCIE_TXVECDB, 0);177}178179static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab)180{181ath11k_pcic_write32(ab, PCIE_TXVECSTATUS, 0);182}183184static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab)185{186ath11k_pcic_write32(ab, PCIE_RXVECDB, 0);187}188189static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab)190{191ath11k_pcic_write32(ab, PCIE_RXVECSTATUS, 0);192}193194void ath11k_mhi_clear_vector(struct ath11k_base *ab)195{196ath11k_mhi_reset_txvecdb(ab);197ath11k_mhi_reset_txvecstatus(ab);198ath11k_mhi_reset_rxvecdb(ab);199ath11k_mhi_reset_rxvecstatus(ab);200}201202static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)203{204struct ath11k_base *ab = ab_pci->ab;205u32 user_base_data, base_vector;206int ret, num_vectors, i;207int *irq;208unsigned int msi_data;209210ret = ath11k_pcic_get_user_msi_assignment(ab, "MHI", &num_vectors,211&user_base_data, &base_vector);212if (ret)213return ret;214215ath11k_dbg(ab, ATH11K_DBG_PCI, "num_vectors %d base_vector %d\n",216num_vectors, base_vector);217218irq = kcalloc(num_vectors, sizeof(int), GFP_KERNEL);219if (!irq)220return -ENOMEM;221222for (i = 0; i < num_vectors; i++) {223msi_data = base_vector;224225if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))226msi_data += i;227228irq[i] = ath11k_pci_get_msi_irq(ab, msi_data);229}230231ab_pci->mhi_ctrl->irq = irq;232ab_pci->mhi_ctrl->nr_irqs = num_vectors;233234return 0;235}236237static int ath11k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl)238{239return 0;240}241242static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)243{244}245246static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)247{248switch (reason) {249case MHI_CB_IDLE:250return "MHI_CB_IDLE";251case MHI_CB_PENDING_DATA:252return "MHI_CB_PENDING_DATA";253case MHI_CB_LPM_ENTER:254return "MHI_CB_LPM_ENTER";255case MHI_CB_LPM_EXIT:256return "MHI_CB_LPM_EXIT";257case MHI_CB_EE_RDDM:258return "MHI_CB_EE_RDDM";259case MHI_CB_EE_MISSION_MODE:260return "MHI_CB_EE_MISSION_MODE";261case MHI_CB_SYS_ERROR:262return "MHI_CB_SYS_ERROR";263case MHI_CB_FATAL_ERROR:264return "MHI_CB_FATAL_ERROR";265case MHI_CB_BW_REQ:266return "MHI_CB_BW_REQ";267default:268return "UNKNOWN";269}270};271272static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,273enum mhi_callback cb)274{275struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);276struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);277278ath11k_dbg(ab, ATH11K_DBG_BOOT, "notify status reason %s\n",279ath11k_mhi_op_callback_to_str(cb));280281switch (cb) {282case MHI_CB_SYS_ERROR:283ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");284break;285case MHI_CB_EE_RDDM:286ath11k_warn(ab, "firmware crashed: MHI_CB_EE_RDDM\n");287if (ab_pci->mhi_pre_cb == MHI_CB_EE_RDDM) {288ath11k_dbg(ab, ATH11K_DBG_BOOT,289"do not queue again for consecutive RDDM event\n");290break;291}292293if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))294queue_work(ab->workqueue_aux, &ab->reset_work);295296break;297default:298break;299}300301ab_pci->mhi_pre_cb = cb;302}303304static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,305void __iomem *addr,306u32 *out)307{308*out = readl(addr);309310return 0;311}312313static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,314void __iomem *addr,315u32 val)316{317writel(val, addr);318}319320static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl)321{322#if defined(__linux__)323struct device_node *np;324struct resource res;325int ret;326327np = of_find_node_by_type(NULL, "memory");328if (!np)329return -ENOENT;330331ret = of_address_to_resource(np, 0, &res);332of_node_put(np);333if (ret)334return ret;335336mhi_ctrl->iova_start = res.start + 0x1000000;337mhi_ctrl->iova_stop = res.end;338339return 0;340#elif defined(__FreeBSD__)341return -ENOENT;342#endif343}344345int ath11k_mhi_register(struct ath11k_pci *ab_pci)346{347struct ath11k_base *ab = ab_pci->ab;348struct mhi_controller *mhi_ctrl;349const struct mhi_controller_config *ath11k_mhi_config;350int ret;351352mhi_ctrl = mhi_alloc_controller();353if (!mhi_ctrl)354return -ENOMEM;355356ab_pci->mhi_ctrl = mhi_ctrl;357mhi_ctrl->cntrl_dev = ab->dev;358mhi_ctrl->regs = ab->mem;359mhi_ctrl->reg_len = ab->mem_len;360361if (ab->fw.amss_data && ab->fw.amss_len > 0) {362/* use MHI firmware file from firmware-N.bin */363mhi_ctrl->fw_data = ab->fw.amss_data;364mhi_ctrl->fw_sz = ab->fw.amss_len;365} else {366/* use the old separate mhi.bin MHI firmware file */367ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE,368ab_pci->amss_path,369sizeof(ab_pci->amss_path));370mhi_ctrl->fw_image = ab_pci->amss_path;371}372373ret = ath11k_mhi_get_msi(ab_pci);374if (ret) {375ath11k_err(ab, "failed to get msi for mhi\n");376goto free_controller;377}378379if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))380mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;381382if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) {383ret = ath11k_mhi_read_addr_from_dt(mhi_ctrl);384if (ret < 0)385goto free_controller;386} else {387mhi_ctrl->iova_start = 0;388mhi_ctrl->iova_stop = ab_pci->dma_mask;389}390391mhi_ctrl->rddm_size = RDDM_DUMP_SIZE;392mhi_ctrl->sbl_size = SZ_512K;393mhi_ctrl->seg_len = SZ_512K;394mhi_ctrl->fbc_download = true;395mhi_ctrl->runtime_get = ath11k_mhi_op_runtime_get;396mhi_ctrl->runtime_put = ath11k_mhi_op_runtime_put;397mhi_ctrl->status_cb = ath11k_mhi_op_status_cb;398mhi_ctrl->read_reg = ath11k_mhi_op_read_reg;399mhi_ctrl->write_reg = ath11k_mhi_op_write_reg;400401switch (ab->hw_rev) {402case ATH11K_HW_QCN9074_HW10:403ath11k_mhi_config = &ath11k_mhi_config_qcn9074;404break;405case ATH11K_HW_QCA6390_HW20:406case ATH11K_HW_WCN6855_HW20:407case ATH11K_HW_WCN6855_HW21:408case ATH11K_HW_QCA2066_HW21:409case ATH11K_HW_QCA6698AQ_HW21:410ath11k_mhi_config = &ath11k_mhi_config_qca6390;411break;412default:413ath11k_err(ab, "failed assign mhi_config for unknown hw rev %d\n",414ab->hw_rev);415ret = -EINVAL;416goto free_controller;417}418419ab_pci->mhi_pre_cb = MHI_CB_INVALID;420ret = mhi_register_controller(mhi_ctrl, ath11k_mhi_config);421if (ret) {422ath11k_err(ab, "failed to register to mhi bus, err = %d\n", ret);423goto free_controller;424}425426return 0;427428free_controller:429mhi_free_controller(mhi_ctrl);430ab_pci->mhi_ctrl = NULL;431return ret;432}433434void ath11k_mhi_unregister(struct ath11k_pci *ab_pci)435{436struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;437438mhi_unregister_controller(mhi_ctrl);439kfree(mhi_ctrl->irq);440mhi_free_controller(mhi_ctrl);441}442443int ath11k_mhi_start(struct ath11k_pci *ab_pci)444{445struct ath11k_base *ab = ab_pci->ab;446int ret;447448ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS;449450ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);451if (ret) {452ath11k_warn(ab, "failed to prepare mhi: %d", ret);453return ret;454}455456ret = mhi_sync_power_up(ab_pci->mhi_ctrl);457if (ret) {458ath11k_warn(ab, "failed to power up mhi: %d", ret);459return ret;460}461462return 0;463}464465void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)466{467/* During suspend we need to use mhi_power_down_keep_dev()468* workaround, otherwise ath11k_core_resume() will timeout469* during resume.470*/471if (is_suspend)472mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true);473else474mhi_power_down(ab_pci->mhi_ctrl, true);475476mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);477}478479int ath11k_mhi_suspend(struct ath11k_pci *ab_pci)480{481struct ath11k_base *ab = ab_pci->ab;482int ret;483484ret = mhi_pm_suspend(ab_pci->mhi_ctrl);485if (ret) {486ath11k_warn(ab, "failed to suspend mhi: %d", ret);487return ret;488}489490return 0;491}492493int ath11k_mhi_resume(struct ath11k_pci *ab_pci)494{495struct ath11k_base *ab = ab_pci->ab;496int ret;497498/* Do force MHI resume as some devices like QCA6390, WCN6855499* are not in M3 state but they are functional. So just ignore500* the MHI state while resuming.501*/502ret = mhi_pm_resume_force(ab_pci->mhi_ctrl);503if (ret) {504ath11k_warn(ab, "failed to resume mhi: %d", ret);505return ret;506}507508return 0;509}510511void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic)512{513mhi_download_rddm_image(mhi_ctrl, in_panic);514}515516517