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

Commit 9baf1aa9 authored by Shahed Shaikh's avatar Shahed Shaikh Committed by David S. Miller
Browse files

qlcnic: Add support for PEX DMA method to read memory section of adapter dump



This patch adds support to read memory section of adapter
dump using PEX DMA method. This method significantly improves
total adapter dump collection time.

Signed-off-by: default avatarShahed Shaikh <shahed.shaikh@qlogic.com>
Signed-off-by: default avatarJitendra Kalsaria <jitendra.kalsaria@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2c4a7878
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -393,6 +393,9 @@ struct qlcnic_fw_dump {
	u32	size;	/* total size of the dump */
	void	*data;	/* dump data area */
	struct	qlcnic_dump_template_hdr *tmpl_hdr;
	dma_addr_t phys_addr;
	void	*dma_buffer;
	bool	use_pex_dma;
};

/*
+218 −7
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#define QLC_83XX_MINIDUMP_FLASH		0x520000
#define QLC_83XX_OCM_INDEX			3
#define QLC_83XX_PCI_INDEX			0
#define QLC_83XX_DMA_ENGINE_INDEX		8

static const u32 qlcnic_ms_read_data[] = {
	0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC
@@ -32,6 +33,16 @@ static const u32 qlcnic_ms_read_data[] = {

#define QLCNIC_DUMP_MASK_MAX	0xff

struct qlcnic_pex_dma_descriptor {
	u32	read_data_size;
	u32	dma_desc_cmd;
	u32	src_addr_low;
	u32	src_addr_high;
	u32	dma_bus_addr_low;
	u32	dma_bus_addr_high;
	u32	rsvd[6];
} __packed;

struct qlcnic_common_entry_hdr {
	u32     type;
	u32     offset;
@@ -90,7 +101,10 @@ struct __ocm {
} __packed;

struct __mem {
	u8	rsvd[24];
	u32	desc_card_addr;
	u32	dma_desc_cmd;
	u32	start_dma_cmd;
	u32	rsvd[3];
	u32	addr;
	u32	size;
} __packed;
@@ -466,12 +480,12 @@ static u32 qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter,
	return l2->no_ops * l2->read_addr_num * sizeof(u32);
}

static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
			      struct qlcnic_dump_entry *entry, __le32 *buffer)
static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter,
					 struct __mem *mem, __le32 *buffer,
					 int *ret)
{
	u32 addr, data, test, ret = 0;
	u32 addr, data, test;
	int i, reg_read;
	struct __mem *mem = &entry->region.mem;

	reg_read = mem->size;
	addr = mem->addr;
@@ -480,7 +494,8 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
		dev_info(&adapter->pdev->dev,
			 "Unaligned memory addr:0x%x size:0x%x\n",
			 addr, reg_read);
		return -EINVAL;
		*ret = -EINVAL;
		return 0;
	}

	mutex_lock(&adapter->ahw->mem_lock);
@@ -499,7 +514,7 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
			if (printk_ratelimit()) {
				dev_err(&adapter->pdev->dev,
					"failed to read through agent\n");
				ret = -EINVAL;
				*ret = -EIO;
				goto out;
			}
		}
@@ -516,6 +531,181 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
	return mem->size;
}

/* DMA register base address */
#define QLC_DMA_REG_BASE_ADDR(dma_no)	(0x77320000 + (dma_no * 0x10000))

/* DMA register offsets w.r.t base address */
#define QLC_DMA_CMD_BUFF_ADDR_LOW	0
#define QLC_DMA_CMD_BUFF_ADDR_HI	4
#define QLC_DMA_CMD_STATUS_CTRL		8

#define QLC_PEX_DMA_READ_SIZE		(PAGE_SIZE * 16)

static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter,
				struct __mem *mem)
{
	struct qlcnic_dump_template_hdr *tmpl_hdr;
	struct device *dev = &adapter->pdev->dev;
	u32 dma_no, dma_base_addr, temp_addr;
	int i, ret, dma_sts;

	tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr;
	dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
	dma_base_addr = QLC_DMA_REG_BASE_ADDR(dma_no);

	temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_LOW;
	ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
					   mem->desc_card_addr);
	if (ret)
		return ret;

	temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_HI;
	ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, 0);
	if (ret)
		return ret;

	temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
	ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
					   mem->start_dma_cmd);
	if (ret)
		return ret;

	/* Wait for DMA to complete */
	temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
	for (i = 0; i < 400; i++) {
		dma_sts = qlcnic_ind_rd(adapter, temp_addr);

		if (dma_sts & BIT_1)
			usleep_range(250, 500);
		else
			break;
	}

	if (i >= 400) {
		dev_info(dev, "PEX DMA operation timed out");
		ret = -EIO;
	}

	return ret;
}

static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
				     struct __mem *mem,
				     __le32 *buffer, int *ret)
{
	struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
	u32 temp, dma_base_addr, size = 0, read_size = 0;
	struct qlcnic_pex_dma_descriptor *dma_descr;
	struct qlcnic_dump_template_hdr *tmpl_hdr;
	struct device *dev = &adapter->pdev->dev;
	dma_addr_t dma_phys_addr;
	void *dma_buffer;

	tmpl_hdr = fw_dump->tmpl_hdr;

	/* Check if DMA engine is available */
	temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
	dma_base_addr = QLC_DMA_REG_BASE_ADDR(temp);
	temp = qlcnic_ind_rd(adapter,
			     dma_base_addr + QLC_DMA_CMD_STATUS_CTRL);

	if (!(temp & BIT_31)) {
		dev_info(dev, "%s: DMA engine is not available\n", __func__);
		*ret = -EIO;
		return 0;
	}

	/* Create DMA descriptor */
	dma_descr = kzalloc(sizeof(struct qlcnic_pex_dma_descriptor),
			    GFP_KERNEL);
	if (!dma_descr) {
		*ret = -ENOMEM;
		return 0;
	}

	/* dma_desc_cmd  0:15  = 0
	 * dma_desc_cmd 16:19  = mem->dma_desc_cmd 0:3
	 * dma_desc_cmd 20:23  = pci function number
	 * dma_desc_cmd 24:31  = mem->dma_desc_cmd 8:15
	 */
	dma_phys_addr = fw_dump->phys_addr;
	dma_buffer = fw_dump->dma_buffer;
	temp = 0;
	temp = mem->dma_desc_cmd & 0xff0f;
	temp |= (adapter->ahw->pci_func & 0xf) << 4;
	dma_descr->dma_desc_cmd = (temp << 16) & 0xffff0000;
	dma_descr->dma_bus_addr_low = LSD(dma_phys_addr);
	dma_descr->dma_bus_addr_high = MSD(dma_phys_addr);
	dma_descr->src_addr_high = 0;

	/* Collect memory dump using multiple DMA operations if required */
	while (read_size < mem->size) {
		if (mem->size - read_size >= QLC_PEX_DMA_READ_SIZE)
			size = QLC_PEX_DMA_READ_SIZE;
		else
			size = mem->size - read_size;

		dma_descr->src_addr_low = mem->addr + read_size;
		dma_descr->read_data_size = size;

		/* Write DMA descriptor to MS memory*/
		temp = sizeof(struct qlcnic_pex_dma_descriptor) / 16;
		*ret = qlcnic_83xx_ms_mem_write128(adapter, mem->desc_card_addr,
						   (u32 *)dma_descr, temp);
		if (*ret) {
			dev_info(dev, "Failed to write DMA descriptor to MS memory at address 0x%x\n",
				 mem->desc_card_addr);
			goto free_dma_descr;
		}

		*ret = qlcnic_start_pex_dma(adapter, mem);
		if (*ret) {
			dev_info(dev, "Failed to start PEX DMA operation\n");
			goto free_dma_descr;
		}

		memcpy(buffer, dma_buffer, size);
		buffer += size / 4;
		read_size += size;
	}

free_dma_descr:
	kfree(dma_descr);

	return read_size;
}

static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
			      struct qlcnic_dump_entry *entry, __le32 *buffer)
{
	struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
	struct device *dev = &adapter->pdev->dev;
	struct __mem *mem = &entry->region.mem;
	u32 data_size;
	int ret = 0;

	if (fw_dump->use_pex_dma) {
		data_size = qlcnic_read_memory_pexdma(adapter, mem, buffer,
						      &ret);
		if (ret)
			dev_info(dev,
				 "Failed to read memory dump using PEX DMA: mask[0x%x]\n",
				 entry->hdr.mask);
		else
			return data_size;
	}

	data_size = qlcnic_read_memory_test_agent(adapter, mem, buffer, &ret);
	if (ret) {
		dev_info(dev,
			 "Failed to read memory dump using test agent method: mask[0x%x]\n",
			 entry->hdr.mask);
		return 0;
	} else {
		return data_size;
	}
}

static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter,
			   struct qlcnic_dump_entry *entry, __le32 *buffer)
{
@@ -893,6 +1083,12 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)

	tmpl_hdr = ahw->fw_dump.tmpl_hdr;
	tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;

	if ((tmpl_hdr->version & 0xffffff) >= 0x20001)
		ahw->fw_dump.use_pex_dma = true;
	else
		ahw->fw_dump.use_pex_dma = false;

	ahw->fw_dump.enable = 1;

	return 0;
@@ -910,7 +1106,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
	struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
	struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr;
	static const struct qlcnic_dump_operations *fw_dump_ops;
	struct device *dev = &adapter->pdev->dev;
	struct qlcnic_hardware_context *ahw;
	void *temp_buffer;

	ahw = adapter->ahw;

@@ -944,6 +1142,16 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
	tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION;
	tmpl_hdr->sys_info[1] = adapter->fw_version;

	if (fw_dump->use_pex_dma) {
		temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE,
						 &fw_dump->phys_addr,
						 GFP_KERNEL);
		if (!temp_buffer)
			fw_dump->use_pex_dma = false;
		else
			fw_dump->dma_buffer = temp_buffer;
	}

	if (qlcnic_82xx_check(adapter)) {
		ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops);
		fw_dump_ops = qlcnic_fw_dump_ops;
@@ -1002,6 +1210,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
		return 0;
	}
error:
	if (fw_dump->use_pex_dma)
		dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
				  fw_dump->dma_buffer, fw_dump->phys_addr);
	vfree(fw_dump->data);
	return -EINVAL;
}