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

Commit 92c2538f authored by Amitkumar Karwar's avatar Amitkumar Karwar Committed by John W. Linville
Browse files

mwifiex: add firmware dump feature for PCIe



Firmware dump feature is added for PCIe based chipsets which can
be used with the help of ethtool commands.

1) Trigger firmware dump operation:
ethtool --set-dump mlan0 0xff

When the operation is completed, udev event will be sent to
trigger external application.

2) Following udev rule can be used to get the data from ethtool:
DRIVER=="mwifiex_pcie", ACTION=="change", RUN+="/sbin/mwifiex_pcie_fw_dump.sh"

mwifiex_pcie_fw_dump.sh: #!/bin/bash
ethtool --set-dump mlan0 0
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/ITCM.log

ethtool --set-dump mlan0 1
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/DTCM.log

ethtool --set-dump mlan0 2
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/SQRAM.log

ethtool --set-dump mlan0 3
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/IRAM.log

Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5f4ef719
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -960,6 +960,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
	if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
		mwifiex_init_fw_complete(adapter);

	if (adapter->if_ops.fw_dump)
		adapter->if_ops.fw_dump(adapter);

	if (adapter->if_ops.card_reset)
		adapter->if_ops.card_reset(adapter);
}
+83 −0
Original line number Diff line number Diff line
@@ -64,7 +64,90 @@ static int mwifiex_ethtool_set_wol(struct net_device *dev,
	return 0;
}

static int
mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	struct mwifiex_adapter *adapter = priv->adapter;
	struct memory_type_mapping *entry;

	if (!adapter->if_ops.fw_dump)
		return -ENOTSUPP;

	dump->flag = adapter->curr_mem_idx;
	dump->version = 1;
	if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
		entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
		dump->len = entry->mem_size;
	} else {
		dump->len = 0;
	}

	return 0;
}

static int
mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump,
		      void *buffer)
{
	u8 *p = buffer;
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	struct mwifiex_adapter *adapter = priv->adapter;
	struct memory_type_mapping *entry;

	if (!adapter->if_ops.fw_dump)
		return -ENOTSUPP;

	if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
		dev_err(adapter->dev, "firmware dump in progress!!\n");
		return -EBUSY;
	}

	entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];

	if (!entry->mem_ptr)
		return -EFAULT;

	memcpy(p, entry->mem_ptr, entry->mem_size);

	entry->mem_size = 0;
	vfree(entry->mem_ptr);
	entry->mem_ptr = NULL;

	return 0;
}

static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val)
{
	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
	struct mwifiex_adapter *adapter = priv->adapter;

	if (!adapter->if_ops.fw_dump)
		return -ENOTSUPP;

	if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
		dev_err(adapter->dev, "firmware dump in progress!!\n");
		return -EBUSY;
	}

	if (val->flag == MWIFIEX_FW_DUMP_IDX) {
		adapter->curr_mem_idx = val->flag;
		adapter->if_ops.fw_dump(adapter);
		return 0;
	}

	if (val->flag < 0 || val->flag >= adapter->num_mem_types)
		return -EINVAL;

	adapter->curr_mem_idx = val->flag;

	return 0;
}

const struct ethtool_ops mwifiex_ethtool_ops = {
	.get_wol = mwifiex_ethtool_get_wol,
	.set_wol = mwifiex_ethtool_set_wol,
	.get_dump_flag = mwifiex_get_dump_flag,
	.get_dump_data = mwifiex_get_dump_data,
	.set_dump = mwifiex_set_dump,
};
+12 −1
Original line number Diff line number Diff line
@@ -382,6 +382,8 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
static void
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
	int idx;

	if (!adapter) {
		pr_err("%s: adapter is NULL\n", __func__);
		return;
@@ -396,7 +398,16 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
	dev_dbg(adapter->dev, "info: free cmd buffer\n");
	mwifiex_free_cmd_buffer(adapter);

	dev_dbg(adapter->dev, "info: free scan table\n");
	for (idx = 0; idx < adapter->num_mem_types; idx++) {
		struct memory_type_mapping *entry =
				&adapter->mem_type_mapping_tbl[idx];

		if (entry->mem_ptr) {
			vfree(entry->mem_ptr);
			entry->mem_ptr = NULL;
		}
		entry->mem_size = 0;
	}

	if (adapter->sleep_cfm)
		dev_kfree_skb_any(adapter->sleep_cfm);
+2 −0
Original line number Diff line number Diff line
@@ -879,6 +879,8 @@ mwifiex_add_card(void *card, struct semaphore *sem,
		goto err_kmalloc;

	INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
	if (adapter->if_ops.iface_work)
		INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);

	/* Register the device. Fill up the private data structure with relevant
	   information from the card. */
+29 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <net/sock.h>
#include <net/lib80211.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
#include <linux/of.h>
@@ -410,6 +411,28 @@ struct mwifiex_roc_cfg {
	struct ieee80211_channel chan;
};

#define MWIFIEX_FW_DUMP_IDX		0xff
#define FW_DUMP_MAX_NAME_LEN		8
#define FW_DUMP_HOST_READY		0xEE
#define FW_DUMP_DONE			0xFF

struct memory_type_mapping {
	u8 mem_name[FW_DUMP_MAX_NAME_LEN];
	u8 *mem_ptr;
	u32 mem_size;
	u8 done_flag;
};

enum rdwr_status {
	RDWR_STATUS_SUCCESS = 0,
	RDWR_STATUS_FAILURE = 1,
	RDWR_STATUS_DONE = 2
};

enum mwifiex_iface_work_flags {
	MWIFIEX_IFACE_WORK_FW_DUMP,
};

struct mwifiex_adapter;
struct mwifiex_private;

@@ -674,6 +697,7 @@ struct mwifiex_if_ops {
	void (*card_reset) (struct mwifiex_adapter *);
	void (*fw_dump)(struct mwifiex_adapter *);
	int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
	void (*iface_work)(struct work_struct *work);
};

struct mwifiex_adapter {
@@ -809,6 +833,11 @@ struct mwifiex_adapter {
	bool ext_scan;
	u8 fw_api_ver;
	u8 fw_key_api_major_ver, fw_key_api_minor_ver;
	struct work_struct iface_work;
	unsigned long iface_work_flags;
	struct memory_type_mapping *mem_type_mapping_tbl;
	u8 num_mem_types;
	u8 curr_mem_idx;
};

int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
Loading