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

Commit d49cdb0a authored by Yue Ma's avatar Yue Ma
Browse files

cnss2: Add mini dump support



Register three WLAN RAM dump images (FBC, RDDM and remote heap)
to mini dump table so that they can be saved into file system
for further debugging.

Change-Id: I80bf0873ed6e6d105a0cf205479de4bb34184f16
Signed-off-by: default avatarYue Ma <yuem@codeaurora.org>
parent 0caad3ca
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/rwsem.h>
#include <linux/suspend.h>
#include <linux/timer.h>
#include <soc/qcom/minidump.h>
#include <soc/qcom/ramdump.h>
#include <soc/qcom/subsystem_notif.h>

@@ -1558,6 +1559,25 @@ static void cnss_driver_event_work(struct work_struct *work)
	cnss_pm_relax(plat_priv);
}

int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma,
		  phys_addr_t *pa, unsigned long attrs)
{
	struct sg_table sgt;
	int ret;

	ret = dma_get_sgtable_attrs(dev, &sgt, va, dma, size, attrs);
	if (ret) {
		cnss_pr_err("Failed to get sgtable for va: 0x%pK, dma: %pa, size: 0x%zx, attrs: 0x%x\n",
			    va, &dma, size, attrs);
		return -EINVAL;
	}

	*pa = page_to_phys(sg_page(sgt.sgl));
	sg_free_table(&sgt);

	return 0;
}

int cnss_register_subsys(struct cnss_plat_data *plat_priv)
{
	int ret = 0;
@@ -1808,6 +1828,46 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv)
	}
}

int cnss_minidump_add_region(struct cnss_plat_data *plat_priv,
			     enum cnss_fw_dump_type type, int seg_no,
			     void *va, phys_addr_t pa, size_t size)
{
	struct md_region md_entry;
	int ret;

	switch (type) {
	case CNSS_FW_IMAGE:
		snprintf(md_entry.name, sizeof(md_entry.name), "FBC_%X",
			 seg_no);
		break;
	case CNSS_FW_RDDM:
		snprintf(md_entry.name, sizeof(md_entry.name), "RDDM_%X",
			 seg_no);
		break;
	case CNSS_FW_REMOTE_HEAP:
		snprintf(md_entry.name, sizeof(md_entry.name), "RHEAP_%X",
			 seg_no);
		break;
	default:
		cnss_pr_err("Unknown dump type ID: %d\n", type);
		return -EINVAL;
	}

	md_entry.phys_addr = pa;
	md_entry.virt_addr = (uintptr_t)va;
	md_entry.size = size;
	md_entry.id = MSM_DUMP_DATA_CNSS_WLAN;

	cnss_pr_dbg("Mini dump region: %s, va: %pK, pa: %pa, size: 0x%zx\n",
		    md_entry.name, va, &pa, size);

	ret = msm_minidump_add_region(&md_entry);
	if (ret)
		cnss_pr_err("Failed to add mini dump region, err = %d\n", ret);

	return ret;
}

static int cnss_register_bus_scale(struct cnss_plat_data *plat_priv)
{
	int ret = 0;
+6 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ struct cnss_fw_mem {
	phys_addr_t pa;
	u8 valid;
	u32 type;
	unsigned long attrs;
};

struct wlfw_rf_chip_info {
@@ -406,5 +407,10 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv);
void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv);
int cnss_get_cpr_info(struct cnss_plat_data *plat_priv);
int cnss_update_cpr_info(struct cnss_plat_data *plat_priv);
int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma,
		  phys_addr_t *pa, unsigned long attrs);
int cnss_minidump_add_region(struct cnss_plat_data *plat_priv,
			     enum cnss_fw_dump_type type, int seg_no,
			     void *va, phys_addr_t pa, size_t size);

#endif /* _CNSS_MAIN_H */
+79 −52
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */

#include <linux/cma.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/memblock.h>
#include <linux/completion.h>
@@ -2738,14 +2740,16 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
	struct device *dev = &pci_priv->pci_dev->dev;
	int i;

	for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
		if (!fw_mem[i].va && fw_mem[i].size) {
			fw_mem[i].va =
				dma_alloc_coherent(&pci_priv->pci_dev->dev,
						   fw_mem[i].size,
						   &fw_mem[i].pa, GFP_KERNEL);
				dma_alloc_attrs(dev, fw_mem[i].size,
						&fw_mem[i].pa, GFP_KERNEL,
						fw_mem[i].attrs);

			if (!fw_mem[i].va) {
				cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx, type: %u\n",
					    fw_mem[i].size, fw_mem[i].type);
@@ -2758,6 +2762,31 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv)
	return 0;
}

static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
	struct device *dev = &pci_priv->pci_dev->dev;
	int i;

	for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
		if (fw_mem[i].va && fw_mem[i].size) {
			cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
				    fw_mem[i].va, &fw_mem[i].pa,
				    fw_mem[i].size, fw_mem[i].type);
			dma_free_attrs(dev, fw_mem[i].size,
				       fw_mem[i].va, fw_mem[i].pa,
				       fw_mem[i].attrs);
			fw_mem[i].va = NULL;
			fw_mem[i].pa = 0;
			fw_mem[i].size = 0;
			fw_mem[i].type = 0;
		}
	}

	plat_priv->fw_mem_seg_len = 0;
}

int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
@@ -2815,30 +2844,6 @@ void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv)
	plat_priv->qdss_mem_seg_len = 0;
}

static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
	int i;

	for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
		if (fw_mem[i].va && fw_mem[i].size) {
			cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
				    fw_mem[i].va, &fw_mem[i].pa,
				    fw_mem[i].size, fw_mem[i].type);
			dma_free_coherent(&pci_priv->pci_dev->dev,
					  fw_mem[i].size, fw_mem[i].va,
					  fw_mem[i].pa);
			fw_mem[i].va = NULL;
			fw_mem[i].pa = 0;
			fw_mem[i].size = 0;
			fw_mem[i].type = 0;
		}
	}

	plat_priv->fw_mem_seg_len = 0;
}

int cnss_pci_load_m3(struct cnss_pci_data *pci_priv)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
@@ -3454,6 +3459,29 @@ int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv)
	return 0;
}

static void cnss_pci_add_dump_seg(struct cnss_pci_data *pci_priv,
				  struct cnss_dump_seg *dump_seg,
				  enum cnss_fw_dump_type type, int seg_no,
				  void *va, dma_addr_t dma, size_t size)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
	struct device *dev = &pci_priv->pci_dev->dev;
	phys_addr_t pa;

	dump_seg->address = dma;
	dump_seg->v_address = va;
	dump_seg->size = size;
	dump_seg->type = type;

	cnss_pr_dbg("Seg: %x, va: %pK, dma: %pa, size: 0x%zx\n",
		    seg_no, va, &dma, size);

	if (cnss_va_to_pa(dev, size, va, dma, &pa, DMA_ATTR_FORCE_CONTIGUOUS))
		return;

	cnss_minidump_add_region(plat_priv, type, seg_no, va, pa, size);
}

void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)
{
	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
@@ -3463,7 +3491,7 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)
		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;
	int ret, i, j;

	if (test_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state)) {
		cnss_pr_dbg("RAM dump is already collected, skip\n");
@@ -3492,13 +3520,10 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)
		    fw_image->entries);

	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);
		cnss_pci_add_dump_seg(pci_priv, dump_seg, CNSS_FW_IMAGE, i,
				      fw_image->mhi_buf[i].buf,
				      fw_image->mhi_buf[i].dma_addr,
				      fw_image->mhi_buf[i].len);
		dump_seg++;
	}

@@ -3508,13 +3533,10 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)
		    rddm_image->entries);

	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);
		cnss_pci_add_dump_seg(pci_priv, dump_seg, CNSS_FW_RDDM, i,
				      rddm_image->mhi_buf[i].buf,
				      rddm_image->mhi_buf[i].dma_addr,
				      rddm_image->mhi_buf[i].len);
		dump_seg++;
	}

@@ -3522,17 +3544,15 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic)

	cnss_pr_dbg("Collect remote heap dump segment\n");

	for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
	for (i = 0, j = 0; i < plat_priv->fw_mem_seg_len; i++) {
		if (fw_mem[i].type == CNSS_MEM_TYPE_DDR) {
			dump_seg->address = fw_mem[i].pa;
			dump_seg->v_address = fw_mem[i].va;
			dump_seg->size = fw_mem[i].size;
			dump_seg->type = CNSS_FW_REMOTE_HEAP;
			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);
			cnss_pci_add_dump_seg(pci_priv, dump_seg,
					      CNSS_FW_REMOTE_HEAP, j,
					      fw_mem[i].va, fw_mem[i].pa,
					      fw_mem[i].size);
			dump_seg++;
			dump_data->nentries++;
			j++;
		}
	}

@@ -3824,12 +3844,12 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	int ret = 0;
	struct cnss_pci_data *pci_priv;
	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
	struct device *dev = &pci_dev->dev;

	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
		    id->vendor, pci_dev->device);

	pci_priv = devm_kzalloc(&pci_dev->dev, sizeof(*pci_priv),
				GFP_KERNEL);
	pci_priv = devm_kzalloc(dev, sizeof(*pci_priv), GFP_KERNEL);
	if (!pci_priv) {
		ret = -ENOMEM;
		goto out;
@@ -3848,6 +3868,13 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
		 DEFAULT_FW_FILE_NAME);
	mutex_init(&pci_priv->bus_lock);

	ret = of_reserved_mem_device_init(dev);
	if (ret)
		cnss_pr_err("Failed to init reserved mem device, err = %d\n",
			    ret);
	if (dev->cma_area)
		cnss_pr_dbg("CMA area is %s\n", cma_get_name(dev->cma_area));

	ret = cnss_register_subsys(plat_priv);
	if (ret)
		goto reset_ctx;
+3 −0
Original line number Diff line number Diff line
@@ -1560,6 +1560,9 @@ static void cnss_wlfw_request_mem_ind_cb(struct qmi_handle *qmi_wlfw,
			    ind_msg->mem_seg[i].size, ind_msg->mem_seg[i].type);
		plat_priv->fw_mem[i].type = ind_msg->mem_seg[i].type;
		plat_priv->fw_mem[i].size = ind_msg->mem_seg[i].size;
		if (plat_priv->fw_mem[i].type == CNSS_MEM_TYPE_DDR)
			plat_priv->fw_mem[i].attrs |=
				DMA_ATTR_FORCE_CONTIGUOUS;
	}

	cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_REQUEST_MEM,