Patchwork [v2,net-next,4/8] qlcnic: Add support for PEX DMA method to read memory section of adapter dump

login
register
mail settings
Submitter Jitendra Kalsaria
Date June 22, 2013, 8:12 a.m.
Message ID <1371888727-16422-5-git-send-email-jitendra.kalsaria@qlogic.com>
Download mbox | patch
Permalink /patch/253359/
State Accepted
Delegated to: David Miller
Headers show

Comments

Jitendra Kalsaria - June 22, 2013, 8:12 a.m.
From: Shahed Shaikh <shahed.shaikh@qlogic.com>

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: Shahed Shaikh <shahed.shaikh@qlogic.com>
Signed-off-by: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
---
 drivers/net/ethernet/qlogic/qlcnic/qlcnic.h        |    3 +
 .../net/ethernet/qlogic/qlcnic/qlcnic_minidump.c   |  225 +++++++++++++++++++-
 2 files changed, 221 insertions(+), 7 deletions(-)

Patch

diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 28a1276..cc2c2c1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -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;
 };
 
 /*
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
index 4b9bab1..ab8a674 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
@@ -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 @@  skip_poll:
 	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 @@  out:
 	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 @@  flash_temp:
 
 	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;
 }