Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eb55626 authored by Yue Ma's avatar Yue Ma Committed by Gerrit - the friendly Code Review server
Browse files

cnss2: Add changes to support MHI bus



Add support to use MHI bus for WLAN QCA6290 chipset. MHI is on top
of PCIe bus and mainly used for downloading firmware and transferring
certain data between host and device.

Change-Id: I5b6993ff120a1568047b288edfb2414e92392fe9
Signed-off-by: default avatarYue Ma <yuem@codeaurora.org>
parent ad8801e6
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -17,8 +17,6 @@
#include "debug.h"
#include "pci.h"

#define CNSS_IPC_LOG_PAGES		32

void *cnss_ipc_log_context;

static int cnss_pin_connect_show(struct seq_file *s, void *data)
@@ -172,7 +170,7 @@ static ssize_t cnss_dev_boot_debug_write(struct file *fp,
	} else if (sysfs_streq(cmd, "shutdown")) {
		ret = cnss_driver_event_post(plat_priv,
					     CNSS_DRIVER_EVENT_POWER_DOWN,
					     CNSS_EVENT_SYNC, NULL);
					     0, NULL);
		clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
	} else {
		cnss_pr_err("Device boot debugfs command is invalid\n");
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,8 @@
#include <linux/ipc_logging.h>
#include <linux/printk.h>

#define CNSS_IPC_LOG_PAGES		32

extern void *cnss_ipc_log_context;

#define cnss_ipc_log_string(_x...) do {					\
+5 −16
Original line number Diff line number Diff line
@@ -940,7 +940,7 @@ static int cnss_register_esoc(struct cnss_plat_data *plat_priv)
		of_property_read_bool(dev->of_node,
				      "qcom,notify-modem-status");

	if (esoc_info->notify_modem_status)
	if (!esoc_info->notify_modem_status)
		goto out;

	ret = of_property_read_string_index(dev->of_node, "esoc-names", 0,
@@ -1172,23 +1172,18 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv)
static void cnss_qca6290_crash_shutdown(struct cnss_plat_data *plat_priv)
{
	struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
	int ret = 0;

	cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n",
		    plat_priv->driver_state);

	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) ||
	    test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) ||
	    test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state))
		return;

	ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM_KERNEL_PANIC);
	if (ret) {
		cnss_pr_err("Fail to complete RDDM, err = %d\n", ret);
	    test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
		cnss_pr_dbg("Ignore crash shutdown\n");
		return;
	}

	cnss_pci_collect_dump_info(pci_priv);
	cnss_pci_collect_dump_info(pci_priv, true);
}

static int cnss_powerup(struct cnss_plat_data *plat_priv)
@@ -1443,7 +1438,6 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv,
	struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
	struct cnss_subsys_info *subsys_info =
		&plat_priv->subsys_info;
	int ret = 0;

	plat_priv->recovery_count++;

@@ -1467,12 +1461,7 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv,
		break;
	case CNSS_REASON_RDDM:
		clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state);
		ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM);
		if (ret) {
			cnss_pr_err("Failed to complete RDDM, err = %d\n", ret);
			break;
		}
		cnss_pci_collect_dump_info(pci_priv);
		cnss_pci_collect_dump_info(pci_priv, false);
		break;
	case CNSS_REASON_DEFAULT:
	case CNSS_REASON_TIMEOUT:
+7 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -109,6 +109,12 @@ struct cnss_fw_mem {
	bool valid;
};

enum cnss_fw_dump_type {
	CNSS_FW_IMAGE,
	CNSS_FW_RDDM,
	CNSS_FW_REMOTE_HEAP,
};

enum cnss_driver_event_type {
	CNSS_DRIVER_EVENT_SERVER_ARRIVE,
	CNSS_DRIVER_EVENT_SERVER_EXIT,
+248 −147
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/memblock.h>

#include "main.h"
#include "debug.h"
@@ -33,13 +34,11 @@

#define PCI_BAR_NUM			0

#ifdef CONFIG_ARM_LPAE
#define PCI_DMA_MASK			64
#else
#define PCI_DMA_MASK			32
#endif
#define PCI_DMA_MASK_32_BIT		32
#define PCI_DMA_MASK_64_BIT		64

#define MHI_NODE_NAME			"qcom,mhi"
#define MHI_MSI_NAME			"MHI"

#define MAX_M3_FILE_NAME_LENGTH		13
#define DEFAULT_M3_FILE_NAME		"m3.bin"
@@ -164,6 +163,14 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv)

	pci_priv->pci_link_state = PCI_LINK_UP;

	if (pci_priv->pci_dev->device != QCA6174_DEVICE_ID) {
		ret = pci_set_power_state(pci_priv->pci_dev, PCI_D0);
		if (ret) {
			cnss_pr_err("Failed to set D0, err = %d\n", ret);
			goto out;
		}
	}

	ret = cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE);
	if (ret)
		goto out;
@@ -209,7 +216,6 @@ int cnss_pci_link_down(struct device *dev)

	cnss_pr_err("PCI link down is detected by host driver, schedule recovery!\n");

	cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_NOTIFY_LINK_ERROR);
	cnss_schedule_recovery(dev, CNSS_REASON_LINK_DOWN);

	return 0;
@@ -324,7 +330,6 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify)
		spin_unlock_irqrestore(&pci_link_down_lock, flags);

		cnss_pr_err("PCI link down, schedule recovery!\n");
		cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_NOTIFY_LINK_ERROR);
		if (pci_dev->device == QCA6174_DEVICE_ID)
			disable_irq(pci_dev->irq);
		cnss_schedule_recovery(&pci_dev->dev, CNSS_REASON_LINK_DOWN);
@@ -830,8 +835,8 @@ static struct cnss_msi_config msi_config = {
	.total_vectors = 32,
	.total_users = 4,
	.users = (struct cnss_msi_user[]) {
		{ .name = "MHI", .num_vectors = 2, .base_vector = 0 },
		{ .name = "CE", .num_vectors = 11, .base_vector = 2 },
		{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
		{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
		{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
		{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
	},
@@ -975,6 +980,7 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
	int ret = 0;
	struct pci_dev *pci_dev = pci_priv->pci_dev;
	u16 device_id;
	u32 pci_dma_mask = PCI_DMA_MASK_64_BIT;

	pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id);
	if (device_id != pci_priv->pci_device_id->device)  {
@@ -1002,17 +1008,20 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
		goto disable_device;
	}

	ret = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(PCI_DMA_MASK));
	if (device_id == QCA6174_DEVICE_ID)
		pci_dma_mask = PCI_DMA_MASK_32_BIT;

	ret = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(pci_dma_mask));
	if (ret) {
		cnss_pr_err("Failed to set PCI DMA mask (%d), err = %d\n",
			    ret, PCI_DMA_MASK);
			    ret, pci_dma_mask);
		goto release_region;
	}

	ret = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(PCI_DMA_MASK));
	ret = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(pci_dma_mask));
	if (ret) {
		cnss_pr_err("Failed to set PCI consistent DMA mask (%d), err = %d\n",
			    ret, PCI_DMA_MASK);
			    ret, pci_dma_mask);
		goto release_region;
	}

@@ -1047,17 +1056,23 @@ static void cnss_pci_disable_bus(struct cnss_pci_data *pci_priv)

	pci_clear_master(pci_dev);
	pci_release_region(pci_dev, PCI_BAR_NUM);
	if (pci_is_enabled(pci_dev))
		pci_disable_device(pci_dev);
}

static int cnss_mhi_pm_runtime_get(struct pci_dev *pci_dev)
static int cnss_mhi_pm_runtime_get(struct mhi_controller *mhi_ctrl, void *priv)
{
	return pm_runtime_get(&pci_dev->dev);
	struct cnss_pci_data *pci_priv = priv;

	return pm_runtime_get(&pci_priv->pci_dev->dev);
}

static void cnss_mhi_pm_runtime_put_noidle(struct pci_dev *pci_dev)
static void cnss_mhi_pm_runtime_put_noidle(struct mhi_controller *mhi_ctrl,
					   void *priv)
{
	pm_runtime_put_noidle(&pci_dev->dev);
	struct cnss_pci_data *pci_priv = priv;

	pm_runtime_put_noidle(&pci_priv->pci_dev->dev);
}

static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state)
@@ -1071,76 +1086,85 @@ static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state)
		return "POWER_ON";
	case CNSS_MHI_POWER_OFF:
		return "POWER_OFF";
	case CNSS_MHI_FORCE_POWER_OFF:
		return "FORCE_POWER_OFF";
	case CNSS_MHI_SUSPEND:
		return "SUSPEND";
	case CNSS_MHI_RESUME:
		return "RESUME";
	case CNSS_MHI_TRIGGER_RDDM:
		return "TRIGGER_RDDM";
	case CNSS_MHI_RDDM:
		return "RDDM";
	case CNSS_MHI_RDDM_KERNEL_PANIC:
		return "RDDM_KERNEL_PANIC";
	case CNSS_MHI_NOTIFY_LINK_ERROR:
		return "NOTIFY_LINK_ERROR";
	default:
		return "UNKNOWN";
	}
};

static void *cnss_pci_collect_dump_seg(struct cnss_pci_data *pci_priv,
				       enum mhi_rddm_segment type,
				       void *start_addr)
void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)
{
	int count;
	struct scatterlist *sg_list, *s;
	unsigned int i;
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_dump_data *dump_data =
		&plat_priv->ramdump_info_v2.dump_data;
	struct cnss_dump_seg *dump_seg = start_addr;
	struct cnss_dump_seg *dump_seg =
		plat_priv->ramdump_info_v2.dump_data_vaddr;
	struct image_info *fw_image, *rddm_image;
	struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
	int ret, i;

	count = mhi_xfer_rddm(&pci_priv->mhi_dev, type, &sg_list);
	if (count <= 0 || !sg_list) {
		cnss_pr_err("Invalid dump_seg for type %u, count %u, sg_list %pK\n",
			    type, count, sg_list);
		return start_addr;
	ret = mhi_download_rddm_img(pci_priv->mhi_ctrl, in_panic);
	if (ret) {
		cnss_pr_err("Failed to download RDDM image, err = %d\n", ret);
		return;
	}

	cnss_pr_dbg("Collect dump seg: type %u, nentries %d\n", type, count);
	fw_image = pci_priv->mhi_ctrl->fbc_image;
	rddm_image = pci_priv->mhi_ctrl->rddm_image;
	dump_data->nentries = 0;

	cnss_pr_dbg("Collect FW image dump segment, nentries %d\n",
		    fw_image->entries);

	for_each_sg(sg_list, s, count, i) {
		dump_seg->address = sg_dma_address(s);
		dump_seg->v_address = sg_virt(s);
		dump_seg->size = s->length;
		dump_seg->type = type;
	for (i = 0; i < fw_image->entries; i++) {
		dump_seg->address = fw_image->mhi_buf[i].dma_addr;
		dump_seg->v_address = fw_image->mhi_buf[i].buf;
		dump_seg->size = fw_image->mhi_buf[i].len;
		dump_seg->type = CNSS_FW_IMAGE;
		cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n",
			    i, dump_seg->address,
			    dump_seg->v_address, dump_seg->size);
		dump_seg++;
	}

	dump_data->nentries += count;
	dump_data->nentries += fw_image->entries;

	return dump_seg;
}
	cnss_pr_dbg("Collect RDDM image dump segment, nentries %d\n",
		    rddm_image->entries);

void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_dump_data *dump_data =
		&plat_priv->ramdump_info_v2.dump_data;
	void *start_addr, *end_addr;
	for (i = 0; i < rddm_image->entries; i++) {
		dump_seg->address = rddm_image->mhi_buf[i].dma_addr;
		dump_seg->v_address = rddm_image->mhi_buf[i].buf;
		dump_seg->size = rddm_image->mhi_buf[i].len;
		dump_seg->type = CNSS_FW_RDDM;
		cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n",
			    i, dump_seg->address,
			    dump_seg->v_address, dump_seg->size);
		dump_seg++;
	}

	dump_data->nentries = 0;
	dump_data->nentries += rddm_image->entries;

	start_addr = plat_priv->ramdump_info_v2.dump_data_vaddr;
	end_addr = cnss_pci_collect_dump_seg(pci_priv,
					     MHI_RDDM_FW_SEGMENT, start_addr);
	if (fw_mem->pa && fw_mem->va && fw_mem->size) {
		cnss_pr_dbg("Collect remote heap dump segment, nentries 1\n");

	start_addr = end_addr;
	end_addr = cnss_pci_collect_dump_seg(pci_priv,
					     MHI_RDDM_RD_SEGMENT, start_addr);
		dump_seg->address = fw_mem->pa;
		dump_seg->v_address = fw_mem->va;
		dump_seg->size = fw_mem->size;
		dump_seg->type = CNSS_FW_REMOTE_HEAP;
		cnss_pr_dbg("seg-0: address 0x%lx, v_address %pK, size 0x%lx\n",
			    dump_seg->address, dump_seg->v_address,
			    dump_seg->size);
		dump_seg++;
		dump_data->nentries++;
	}

	if (dump_data->nentries > 0)
		plat_priv->ramdump_info_v2.dump_data_valid = true;
@@ -1154,65 +1178,144 @@ void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv)
	plat_priv->ramdump_info_v2.dump_data_valid = false;
}

static void cnss_mhi_notify_status(enum MHI_CB_REASON reason, void *priv)
static int cnss_mhi_link_status(struct mhi_controller *mhi_ctrl, void *priv)
{
	struct cnss_pci_data *pci_priv = priv;
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	enum cnss_recovery_reason cnss_reason = CNSS_REASON_RDDM;
	u16 device_id;

	if (!pci_priv)
	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL\n");
		return -EINVAL;
	}

	pci_read_config_word(pci_priv->pci_dev, PCI_DEVICE_ID, &device_id);
	if (device_id != pci_priv->device_id)  {
		cnss_pr_err("PCI device ID mismatch, link possibly down, current read ID: 0x%x, record ID: 0x%x\n",
			    device_id, pci_priv->device_id);
		return -EIO;
	}

	return 0;
}

static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, void *priv,
				   enum MHI_CB reason)
{
	struct cnss_pci_data *pci_priv = priv;
	struct cnss_plat_data *plat_priv;
	enum cnss_recovery_reason cnss_reason;

	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL");
		return;
	}

	plat_priv = pci_priv->plat_priv;

	cnss_pr_dbg("MHI status cb is called with reason %d\n", reason);

	switch (reason) {
	case MHI_CB_EE_RDDM:
		cnss_reason = CNSS_REASON_RDDM;
		break;
	default:
		cnss_pr_err("Unsupported MHI status cb reason: %d\n", reason);
		return;
	}

	set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state);
	del_timer(&plat_priv->fw_boot_timer);

	if (reason == MHI_CB_SYS_ERROR)
		cnss_reason = CNSS_REASON_TIMEOUT;

	cnss_schedule_recovery(&pci_priv->pci_dev->dev,
			       cnss_reason);
}

static int cnss_pci_get_mhi_msi(struct cnss_pci_data *pci_priv)
{
	int ret, num_vectors, i;
	u32 user_base_data, base_vector;
	int *irq;

	ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev,
					   MHI_MSI_NAME, &num_vectors,
					   &user_base_data, &base_vector);
	if (ret)
		return ret;

	cnss_pr_dbg("Number of assigned MSI for MHI is %d, base vector is %d\n",
		    num_vectors, base_vector);

	irq = kcalloc(num_vectors, sizeof(int), GFP_KERNEL);
	if (!irq)
		return -ENOMEM;

	for (i = 0; i < num_vectors; i++)
		irq[i] = cnss_get_msi_irq(&pci_priv->pci_dev->dev,
					  base_vector + i);

	pci_priv->mhi_ctrl->irq = irq;
	pci_priv->mhi_ctrl->msi_allocated = num_vectors;

	return 0;
}

static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
{
	int ret = 0;
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct pci_dev *pci_dev = pci_priv->pci_dev;
	struct mhi_device *mhi_dev = &pci_priv->mhi_dev;
	struct mhi_controller *mhi_ctrl;

	mhi_ctrl = mhi_alloc_controller(0);
	if (!mhi_ctrl) {
		cnss_pr_err("Invalid MHI controller context\n");
		return -EINVAL;
	}

	mhi_dev->dev = &pci_priv->plat_priv->plat_dev->dev;
	mhi_dev->pci_dev = pci_dev;
	pci_priv->mhi_ctrl = mhi_ctrl;

	mhi_dev->resources[0].start = (resource_size_t)pci_priv->bar;
	mhi_dev->resources[0].end = (resource_size_t)pci_priv->bar +
		pci_resource_len(pci_dev, PCI_BAR_NUM);
	mhi_dev->resources[0].flags =
		pci_resource_flags(pci_dev, PCI_BAR_NUM);
	mhi_dev->resources[0].name = "BAR";
	cnss_pr_dbg("BAR start is %pa, BAR end is %pa\n",
		    &mhi_dev->resources[0].start, &mhi_dev->resources[0].end);
	mhi_ctrl->priv_data = pci_priv;
	mhi_ctrl->dev = &pci_dev->dev;
	mhi_ctrl->of_node = (&plat_priv->plat_dev->dev)->of_node;
	mhi_ctrl->dev_id = pci_priv->device_id;
	mhi_ctrl->domain = pci_domain_nr(pci_dev->bus);
	mhi_ctrl->bus = pci_dev->bus->number;
	mhi_ctrl->slot = PCI_SLOT(pci_dev->devfn);

	if (!mhi_dev->resources[1].start) {
		mhi_dev->resources[1].start = pci_dev->irq;
		mhi_dev->resources[1].end = pci_dev->irq + 1;
		mhi_dev->resources[1].flags = IORESOURCE_IRQ;
		mhi_dev->resources[1].name = "IRQ";
	mhi_ctrl->regs = pci_priv->bar;
	cnss_pr_dbg("BAR starts at %pa\n",
		    &pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM));

	ret = cnss_pci_get_mhi_msi(pci_priv);
	if (ret) {
		cnss_pr_err("Failed to get MSI for MHI\n");
		return ret;
	}
	cnss_pr_dbg("IRQ start is %pa, IRQ end is %pa\n",
		    &mhi_dev->resources[1].start, &mhi_dev->resources[1].end);

	mhi_dev->pm_runtime_get = cnss_mhi_pm_runtime_get;
	mhi_dev->pm_runtime_put_noidle = cnss_mhi_pm_runtime_put_noidle;
	if (pci_priv->smmu_s1_enable) {
		mhi_ctrl->iova_start = pci_priv->smmu_iova_start;
		mhi_ctrl->iova_stop = pci_priv->smmu_iova_start +
					pci_priv->smmu_iova_len;
	} else {
		mhi_ctrl->iova_start = memblock_start_of_DRAM();
		mhi_ctrl->iova_stop = memblock_end_of_DRAM();
	}

	mhi_ctrl->link_status = cnss_mhi_link_status;
	mhi_ctrl->status_cb = cnss_mhi_notify_status;
	mhi_ctrl->runtime_get = cnss_mhi_pm_runtime_get;
	mhi_ctrl->runtime_put = cnss_mhi_pm_runtime_put_noidle;

	mhi_ctrl->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size;

	mhi_dev->support_rddm = true;
	mhi_dev->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size;
	mhi_dev->status_cb = cnss_mhi_notify_status;
	mhi_ctrl->log_buf = ipc_log_context_create(CNSS_IPC_LOG_PAGES,
						   "cnss-mhi", 0);
	if (!mhi_ctrl->log_buf)
		cnss_pr_err("Unable to create CNSS MHI IPC log context\n");

	ret = mhi_register_device(mhi_dev, MHI_NODE_NAME, pci_priv);
	ret = of_register_mhi_controller(mhi_ctrl);
	if (ret) {
		cnss_pr_err("Failed to register as MHI device, err = %d\n",
			    ret);
		cnss_pr_err("Failed to register to MHI bus, err = %d\n", ret);
		return ret;
	}

@@ -1221,35 +1324,11 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)

static void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv)
{
}
	struct mhi_controller *mhi_ctrl = pci_priv->mhi_ctrl;

static enum mhi_dev_ctrl cnss_to_mhi_dev_state(enum cnss_mhi_state state)
{
	switch (state) {
	case CNSS_MHI_INIT:
		return MHI_DEV_CTRL_INIT;
	case CNSS_MHI_DEINIT:
		return MHI_DEV_CTRL_DE_INIT;
	case CNSS_MHI_POWER_ON:
		return MHI_DEV_CTRL_POWER_ON;
	case CNSS_MHI_POWER_OFF:
		return MHI_DEV_CTRL_POWER_OFF;
	case CNSS_MHI_SUSPEND:
		return MHI_DEV_CTRL_SUSPEND;
	case CNSS_MHI_RESUME:
		return MHI_DEV_CTRL_RESUME;
	case CNSS_MHI_TRIGGER_RDDM:
		return MHI_DEV_CTRL_TRIGGER_RDDM;
	case CNSS_MHI_RDDM:
		return MHI_DEV_CTRL_RDDM;
	case CNSS_MHI_RDDM_KERNEL_PANIC:
		return MHI_DEV_CTRL_RDDM_KERNEL_PANIC;
	case CNSS_MHI_NOTIFY_LINK_ERROR:
		return MHI_DEV_CTRL_NOTIFY_LINK_ERROR;
	default:
		cnss_pr_err("Unknown CNSS MHI state (%d)\n", state);
		return -EINVAL;
	}
	mhi_unregister_mhi_controller(mhi_ctrl);
	ipc_log_context_destroy(mhi_ctrl->log_buf);
	kfree(mhi_ctrl->irq);
}

static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv,
@@ -1266,6 +1345,10 @@ static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv,
		    !test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state))
			return 0;
		break;
	case CNSS_MHI_FORCE_POWER_OFF:
		if (test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state))
			return 0;
		break;
	case CNSS_MHI_POWER_OFF:
	case CNSS_MHI_SUSPEND:
		if (test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state) &&
@@ -1277,9 +1360,6 @@ static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv,
			return 0;
		break;
	case CNSS_MHI_TRIGGER_RDDM:
	case CNSS_MHI_RDDM:
	case CNSS_MHI_RDDM_KERNEL_PANIC:
	case CNSS_MHI_NOTIFY_LINK_ERROR:
		return 0;
	default:
		cnss_pr_err("Unhandled MHI state: %s(%d)\n",
@@ -1307,6 +1387,7 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv,
		set_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state);
		break;
	case CNSS_MHI_POWER_OFF:
	case CNSS_MHI_FORCE_POWER_OFF:
		clear_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state);
		break;
	case CNSS_MHI_SUSPEND:
@@ -1316,9 +1397,6 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv,
		clear_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state);
		break;
	case CNSS_MHI_TRIGGER_RDDM:
	case CNSS_MHI_RDDM:
	case CNSS_MHI_RDDM_KERNEL_PANIC:
	case CNSS_MHI_NOTIFY_LINK_ERROR:
		break;
	default:
		cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state);
@@ -1329,7 +1407,6 @@ int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
			   enum cnss_mhi_state mhi_state)
{
	int ret = 0;
	enum mhi_dev_ctrl mhi_dev_state = cnss_to_mhi_dev_state(mhi_state);

	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL!\n");
@@ -1339,8 +1416,8 @@ int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
	if (pci_priv->device_id == QCA6174_DEVICE_ID)
		return 0;

	if (mhi_dev_state < 0) {
		cnss_pr_err("Invalid MHI DEV state (%d)\n", mhi_dev_state);
	if (mhi_state < 0) {
		cnss_pr_err("Invalid MHI state (%d)\n", mhi_state);
		return -EINVAL;
	}

@@ -1350,16 +1427,51 @@ int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,

	cnss_pr_dbg("Setting MHI state: %s(%d)\n",
		    cnss_mhi_state_to_str(mhi_state), mhi_state);
	ret = mhi_pm_control_device(&pci_priv->mhi_dev, mhi_dev_state);
	if (ret) {
		cnss_pr_err("Failed to set MHI state: %s(%d)\n",

	switch (mhi_state) {
	case CNSS_MHI_INIT:
		ret = mhi_prepare_for_power_up(pci_priv->mhi_ctrl);
		break;
	case CNSS_MHI_DEINIT:
		mhi_unprepare_after_power_down(pci_priv->mhi_ctrl);
		ret = 0;
		break;
	case CNSS_MHI_POWER_ON:
		ret = mhi_sync_power_up(pci_priv->mhi_ctrl);
		break;
	case CNSS_MHI_POWER_OFF:
		mhi_power_down(pci_priv->mhi_ctrl, true);
		ret = 0;
		break;
	case CNSS_MHI_FORCE_POWER_OFF:
		mhi_power_down(pci_priv->mhi_ctrl, false);
		ret = 0;
		break;
	case CNSS_MHI_SUSPEND:
		ret = mhi_pm_suspend(pci_priv->mhi_ctrl);
		break;
	case CNSS_MHI_RESUME:
		ret = mhi_pm_resume(pci_priv->mhi_ctrl);
		break;
	case CNSS_MHI_TRIGGER_RDDM:
		cnss_pr_dbg("Bypass MHI state: %s(%d)\n",
			    cnss_mhi_state_to_str(mhi_state), mhi_state);
		goto out;
		break;
	default:
		cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state);
		ret = -EINVAL;
	}

	if (ret)
		goto out;

	cnss_pci_set_mhi_state_bit(pci_priv, mhi_state);

	return 0;

out:
	cnss_pr_err("Failed to set MHI state: %s(%d)\n",
		    cnss_mhi_state_to_str(mhi_state), mhi_state);
	return ret;
}

@@ -1404,7 +1516,10 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv)
	plat_priv = pci_priv->plat_priv;

	cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_RESUME);
	if (!pci_priv->pci_link_down_ind)
		cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_OFF);
	else
		cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_FORCE_POWER_OFF);

	if (plat_priv->ramdump_info_v2.dump_data_valid ||
	    test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state))
@@ -1424,20 +1539,6 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
		    id->vendor, pci_dev->device);

	switch (pci_dev->device) {
	case QCA6290_EMULATION_DEVICE_ID:
	case QCA6290_DEVICE_ID:
		if (!mhi_is_device_ready(&plat_priv->plat_dev->dev,
					 MHI_NODE_NAME)) {
			cnss_pr_err("MHI driver is not ready, defer PCI probe!\n");
			ret = -EPROBE_DEFER;
			goto out;
		}
		break;
	default:
		break;
	}

	pci_priv = devm_kzalloc(&pci_dev->dev, sizeof(*pci_priv),
				GFP_KERNEL);
	if (!pci_priv) {
Loading