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

Commit 7a7e4178 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "cnss2: Support collecting firmware dump during driver load"

parents 4a833eda 8bead6d8
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 {					\
+17 −24
Original line number Diff line number Diff line
@@ -571,7 +571,8 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv)
		goto out;
	}

	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
	    test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
		ret = plat_priv->driver_ops->reinit(pci_priv->pci_dev,
						    pci_priv->pci_device_id);
		if (ret) {
@@ -588,6 +589,7 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv)
				    ret);
			goto out;
		}
		clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
		clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
		set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
	}
@@ -614,7 +616,8 @@ static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv)
		return -EINVAL;
	}

	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
	    test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
		plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
	} else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
		plat_priv->driver_ops->remove(pci_priv->pci_dev);
@@ -652,7 +655,9 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
		complete(&plat_priv->power_up_complete);
	}

	if (ret)
	if (ret && test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state))
		goto out;
	else if (ret)
		goto shutdown;

	return 0;
@@ -662,6 +667,10 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
	cnss_suspend_pci_link(plat_priv->bus_priv);
	cnss_power_off_device(plat_priv);

	clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
	clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);

out:
	return ret;
}

@@ -940,7 +949,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 +1181,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 +1447,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 +1470,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:
@@ -1538,11 +1536,6 @@ static int cnss_driver_recovery_hdlr(struct cnss_plat_data *plat_priv,
		if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
			set_bit(CNSS_FW_BOOT_RECOVERY,
				&plat_priv->driver_state);
		} else if (test_bit(CNSS_DRIVER_LOADING,
			   &plat_priv->driver_state)) {
			cnss_pr_err("Driver probe is in progress, ignore recovery\n");
			ret = -EINVAL;
			goto out;
		}
		break;
	}
+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,
+252 −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,148 @@ 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);

	if (plat_priv->driver_ops && plat_priv->driver_ops->update_status)
		plat_priv->driver_ops->update_status(pci_priv->pci_dev,
						     CNSS_FW_DOWN);

	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 +1328,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 +1349,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 +1364,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 +1391,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 +1401,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 +1411,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 +1420,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 +1431,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 +1520,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 +1543,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