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

Commit 7eb9855d authored by Amit Kumar Salecha's avatar Amit Kumar Salecha Committed by David S. Miller
Browse files

qlcnic: add interrupt diagnostic test



Interrupt test (offline) added in ethtool self test.
Register a temporary interrupt handler and then send command to fw
to raise an interrupt.

Signed-off-by: default avatarSucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: default avatarAmit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 897d3596
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -560,6 +560,8 @@ struct qlcnic_recv_context {
/*
 * Context state
 */
#define QLCHAL_VERSION	1

#define QLCNIC_HOST_CTX_STATE_ACTIVE	2

/*
@@ -894,6 +896,8 @@ struct qlcnic_mac_req {
#define __QLCNIC_RESETTING		2
#define __QLCNIC_START_FW 		4

#define QLCNIC_INTERRUPT_TEST		1

struct qlcnic_adapter {
	struct qlcnic_hardware_context ahw;

@@ -946,9 +950,10 @@ struct qlcnic_adapter {
	u32 heartbit;

	u8 dev_state;
	u8 diag_test;
	u8 diag_cnt;
	u8 rsrd1;
	u32 rsrd2;

	u16 rsrd2;

	u8 mac_addr[ETH_ALEN];

@@ -1064,6 +1069,10 @@ int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac);

/* Functions from qlcnic_main.c */
int qlcnic_reset_context(struct qlcnic_adapter *);
u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
	u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd);
void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
int qlcnic_diag_alloc_res(struct net_device *netdev, int test);

/*
 * QLOGIC Board information
+1 −3
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@

#include "qlcnic.h"

#define QLCHAL_VERSION	1

static u32
qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
{
@@ -45,7 +43,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
	return rsp;
}

static u32
u32
qlcnic_issue_cmd(struct qlcnic_adapter *adapter,
	u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd)
{
+40 −1
Original line number Diff line number Diff line
@@ -65,7 +65,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = {

static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
	"Register_Test_on_offline",
	"Link_Test_on_offline"
	"Link_Test_on_offline",
	"Interrupt_Test_offline"
};

#define QLCNIC_TEST_LEN	ARRAY_SIZE(qlcnic_gstrings_test)
@@ -613,12 +614,50 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset)
	}
}

static int qlcnic_irq_test(struct net_device *netdev)
{
	struct qlcnic_adapter *adapter = netdev_priv(netdev);
	int max_sds_rings = adapter->max_sds_rings;
	int ret;

	if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
		return -EIO;

	ret = qlcnic_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST);
	if (ret)
		goto clear_it;

	adapter->diag_cnt = 0;
	ret = qlcnic_issue_cmd(adapter, adapter->ahw.pci_func,
			QLCHAL_VERSION, adapter->portnum, 0, 0, 0x00000011);
	if (ret)
		goto done;

	msleep(10);

	ret = !adapter->diag_cnt;

done:
	qlcnic_diag_free_res(netdev, max_sds_rings);

clear_it:
	adapter->max_sds_rings = max_sds_rings;
	clear_bit(__QLCNIC_RESETTING, &adapter->state);
	return ret;
}

static void
qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test,
		     u64 *data)
{
	memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN);

	if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
		data[2] = qlcnic_irq_test(dev);
		if (data[2])
			eth_test->flags |= ETH_TEST_FL_FAILED;
	}

	data[0] = qlcnic_reg_test(dev);
	if (data[0])
		eth_test->flags |= ETH_TEST_FL_FAILED;
+102 −10
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter);
static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter);
static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter);

static irqreturn_t qlcnic_tmp_intr(int irq, void *data);
static irqreturn_t qlcnic_intr(int irq, void *data);
static irqreturn_t qlcnic_msi_intr(int irq, void *data);
static irqreturn_t qlcnic_msix_intr(int irq, void *data);
@@ -720,6 +721,12 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
	struct net_device *netdev = adapter->netdev;
	struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;

	if (adapter->diag_test == QLCNIC_INTERRUPT_TEST) {
		handler = qlcnic_tmp_intr;
		if (!QLCNIC_IS_MSI_FAMILY(adapter))
			flags |= IRQF_SHARED;

	} else {
		if (adapter->flags & QLCNIC_MSIX_ENABLED)
			handler = qlcnic_msix_intr;
		else if (adapter->flags & QLCNIC_MSI_ENABLED)
@@ -728,6 +735,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
			flags |= IRQF_SHARED;
			handler = qlcnic_intr;
		}
	}
	adapter->irq = netdev->irq;

	for (ring = 0; ring < adapter->max_sds_rings; ring++) {
@@ -923,6 +931,60 @@ qlcnic_detach(struct qlcnic_adapter *adapter)
	adapter->is_up = 0;
}

void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
{
	struct qlcnic_adapter *adapter = netdev_priv(netdev);
	struct qlcnic_host_sds_ring *sds_ring;
	int ring;

	for (ring = 0; ring < adapter->max_sds_rings; ring++) {
		sds_ring = &adapter->recv_ctx.sds_rings[ring];
		qlcnic_disable_int(sds_ring);
	}

	qlcnic_detach(adapter);

	adapter->diag_test = 0;
	adapter->max_sds_rings = max_sds_rings;

	if (qlcnic_attach(adapter))
		return;

	if (netif_running(netdev))
		__qlcnic_up(adapter, netdev);

	netif_device_attach(netdev);
}

int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
{
	struct qlcnic_adapter *adapter = netdev_priv(netdev);
	struct qlcnic_host_sds_ring *sds_ring;
	int ring;
	int ret;

	netif_device_detach(netdev);

	if (netif_running(netdev))
		__qlcnic_down(adapter, netdev);

	qlcnic_detach(adapter);

	adapter->max_sds_rings = 1;
	adapter->diag_test = test;

	ret = qlcnic_attach(adapter);
	if (ret)
		return ret;

	for (ring = 0; ring < adapter->max_sds_rings; ring++) {
		sds_ring = &adapter->recv_ctx.sds_rings[ring];
		qlcnic_enable_int(sds_ring);
	}

	return 0;
}

int
qlcnic_reset_context(struct qlcnic_adapter *adapter)
{
@@ -1689,10 +1751,8 @@ static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev)
	return stats;
}

static irqreturn_t qlcnic_intr(int irq, void *data)
static irqreturn_t qlcnic_clear_legacy_intr(struct qlcnic_adapter *adapter)
{
	struct qlcnic_host_sds_ring *sds_ring = data;
	struct qlcnic_adapter *adapter = sds_ring->adapter;
	u32 status;

	status = readl(adapter->isr_int_vec);
@@ -1710,6 +1770,38 @@ static irqreturn_t qlcnic_intr(int irq, void *data)
	readl(adapter->isr_int_vec);
	readl(adapter->isr_int_vec);

	return IRQ_HANDLED;
}

static irqreturn_t qlcnic_tmp_intr(int irq, void *data)
{
	struct qlcnic_host_sds_ring *sds_ring = data;
	struct qlcnic_adapter *adapter = sds_ring->adapter;

	if (adapter->flags & QLCNIC_MSIX_ENABLED)
		goto done;
	else if (adapter->flags & QLCNIC_MSI_ENABLED) {
		writel(0xffffffff, adapter->tgt_status_reg);
		goto done;
	}

	if (qlcnic_clear_legacy_intr(adapter) == IRQ_NONE)
		return IRQ_NONE;

done:
	adapter->diag_cnt++;
	qlcnic_enable_int(sds_ring);
	return IRQ_HANDLED;
}

static irqreturn_t qlcnic_intr(int irq, void *data)
{
	struct qlcnic_host_sds_ring *sds_ring = data;
	struct qlcnic_adapter *adapter = sds_ring->adapter;

	if (qlcnic_clear_legacy_intr(adapter) == IRQ_NONE)
		return IRQ_NONE;

	napi_schedule(&sds_ring->napi);

	return IRQ_HANDLED;