Commit 04e715cd authored by Samuel Ortiz's avatar Samuel Ortiz Committed by John W. Linville
Browse files

iwmc3200wifi: Add a last_fw_err debugfs entry



In order to check what was the last fw error we got accross resets, we add
this debugfs entry. It displays the complete ASSERT information.

Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d210176e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -108,6 +108,8 @@ struct iwm_debugfs {
	struct dentry *txq_dentry;
	struct dentry *tx_credit_dentry;
	struct dentry *rx_ticket_dentry;

	struct dentry *fw_err_dentry;
};

#ifdef CONFIG_IWM_DEBUG
+101 −4
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
			iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
			"%llu\n");

static int iwm_txrx_open(struct inode *inode, struct file *filp)
static int iwm_generic_open(struct inode *inode, struct file *filp)
{
	filp->private_data = inode->i_private;
	return 0;
@@ -289,25 +289,111 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
	return ret;
}

static ssize_t iwm_debugfs_fw_err_read(struct file *filp,
				       char __user *buffer,
				       size_t count, loff_t *ppos)
{

	struct iwm_priv *iwm = filp->private_data;
	char buf[512];
	int buf_len = 512;
	size_t len = 0;

	if (*ppos != 0)
		return 0;
	if (count < sizeof(buf))
		return -ENOSPC;

	if (!iwm->last_fw_err)
		return -ENOMEM;

	if (iwm->last_fw_err->line_num == 0)
		goto out;

	len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n",
	     (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC)
			? 'L' : 'U');
	len += snprintf(buf + len, buf_len - len,
			"\tCategory:    %d\n",
			le32_to_cpu(iwm->last_fw_err->category));

	len += snprintf(buf + len, buf_len - len,
			"\tStatus:      0x%x\n",
			le32_to_cpu(iwm->last_fw_err->status));

	len += snprintf(buf + len, buf_len - len,
			"\tPC:          0x%x\n",
			le32_to_cpu(iwm->last_fw_err->pc));

	len += snprintf(buf + len, buf_len - len,
			"\tblink1:      %d\n",
			le32_to_cpu(iwm->last_fw_err->blink1));

	len += snprintf(buf + len, buf_len - len,
			"\tblink2:      %d\n",
			le32_to_cpu(iwm->last_fw_err->blink2));

	len += snprintf(buf + len, buf_len - len,
			"\tilink1:      %d\n",
			le32_to_cpu(iwm->last_fw_err->ilink1));

	len += snprintf(buf + len, buf_len - len,
			"\tilink2:      %d\n",
			le32_to_cpu(iwm->last_fw_err->ilink2));

	len += snprintf(buf + len, buf_len - len,
			"\tData1:       0x%x\n",
			le32_to_cpu(iwm->last_fw_err->data1));

	len += snprintf(buf + len, buf_len - len,
			"\tData2:       0x%x\n",
			le32_to_cpu(iwm->last_fw_err->data2));

	len += snprintf(buf + len, buf_len - len,
			"\tLine number: %d\n",
			le32_to_cpu(iwm->last_fw_err->line_num));

	len += snprintf(buf + len, buf_len - len,
			"\tUMAC status: 0x%x\n",
			le32_to_cpu(iwm->last_fw_err->umac_status));

	len += snprintf(buf + len, buf_len - len,
			"\tLMAC status: 0x%x\n",
			le32_to_cpu(iwm->last_fw_err->lmac_status));

	len += snprintf(buf + len, buf_len - len,
			"\tSDIO status: 0x%x\n",
			le32_to_cpu(iwm->last_fw_err->sdio_status));

out:

	return simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
}

static const struct file_operations iwm_debugfs_txq_fops = {
	.owner =	THIS_MODULE,
	.open =		iwm_txrx_open,
	.open =		iwm_generic_open,
	.read =		iwm_debugfs_txq_read,
};

static const struct file_operations iwm_debugfs_tx_credit_fops = {
	.owner =	THIS_MODULE,
	.open =		iwm_txrx_open,
	.open =		iwm_generic_open,
	.read =		iwm_debugfs_tx_credit_read,
};

static const struct file_operations iwm_debugfs_rx_ticket_fops = {
	.owner =	THIS_MODULE,
	.open =		iwm_txrx_open,
	.open =		iwm_generic_open,
	.read =		iwm_debugfs_rx_ticket_read,
};

static const struct file_operations iwm_debugfs_fw_err_fops = {
	.owner =	THIS_MODULE,
	.open =		iwm_generic_open,
	.read =		iwm_debugfs_fw_err_read,
};

int iwm_debugfs_init(struct iwm_priv *iwm)
{
	int i, result;
@@ -423,6 +509,16 @@ int iwm_debugfs_init(struct iwm_priv *iwm)
		goto error;
	}

	iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
						     iwm->dbg.dbgdir, iwm,
						     &iwm_debugfs_fw_err_fops);
	result = PTR_ERR(iwm->dbg.fw_err_dentry);
	if (IS_ERR(iwm->dbg.fw_err_dentry) && (result != -ENODEV)) {
		IWM_ERR(iwm, "Couldn't create last FW err: %d\n", result);
		goto error;
	}


	return 0;

 error:
@@ -441,6 +537,7 @@ void iwm_debugfs_exit(struct iwm_priv *iwm)
	debugfs_remove(iwm->dbg.txq_dentry);
	debugfs_remove(iwm->dbg.tx_credit_dentry);
	debugfs_remove(iwm->dbg.rx_ticket_dentry);
	debugfs_remove(iwm->dbg.fw_err_dentry);
	if (iwm->bus_ops->debugfs_exit)
		iwm->bus_ops->debugfs_exit(iwm);

+2 −0
Original line number Diff line number Diff line
@@ -289,6 +289,8 @@ struct iwm_priv {
	u8 *resp_ie;
	int resp_ie_len;

	struct iwm_fw_error_hdr *last_fw_err;

	char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};

+6 −0
Original line number Diff line number Diff line
@@ -260,6 +260,11 @@ int iwm_priv_init(struct iwm_priv *iwm)
	iwm->watchdog.data = (unsigned long)iwm;
	mutex_init(&iwm->mutex);

	iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr),
				   GFP_KERNEL);
	if (iwm->last_fw_err == NULL)
		return -ENOMEM;

	return 0;
}

@@ -271,6 +276,7 @@ void iwm_priv_deinit(struct iwm_priv *iwm)
		destroy_workqueue(iwm->txq[i].wq);

	destroy_workqueue(iwm->rx_wq);
	kfree(iwm->last_fw_err);
}

/*
+2 −0
Original line number Diff line number Diff line
@@ -102,6 +102,8 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
	error = (struct iwm_umac_notif_error *)buf;
	fw_err = &error->err;

	memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr));

	IWM_ERR(iwm, "%cMAC FW ERROR:\n",
	 (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
	IWM_ERR(iwm, "\tCategory:    %d\n", le32_to_cpu(fw_err->category));