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

Commit 629263ac authored by Sony Chacko's avatar Sony Chacko Committed by David S. Miller
Browse files

qlcnic: 83xx CNA inter driver communication mechanism



Inter Driver Communication (IDC) module.
CNA function drivers(ISCSI, FCOE and NIC) which shares the adapter
relies on IDC mechanism for gracefull shut down, restart and
firmware error recovery.

Signed-off-by: default avatarRajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: default avatarSucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: default avatarSony Chacko <sony.chacko@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d865ebb4
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -6,4 +6,5 @@ obj-$(CONFIG_QLCNIC) := qlcnic.o

qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
	qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \
	qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o
	qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \
	qlcnic_83xx_init.o
+14 −0
Original line number Diff line number Diff line
@@ -299,6 +299,12 @@ struct qlcnic_fdt {

extern char qlcnic_driver_name[];

extern int qlcnic_use_msi;
extern int qlcnic_use_msi_x;
extern int qlcnic_auto_fw_reset;
extern int qlcnic_load_fw_file;
extern int qlcnic_config_npars;

/* Number of status descriptors to handle per interrupt */
#define MAX_STATUS_HANDLE	(64)

@@ -436,6 +442,8 @@ struct qlcnic_hardware_context {
	struct qlcnic_nic_intr_coalesce coal;
	struct qlcnic_fw_dump fw_dump;
	struct qlcnic_fdt fdt;
	struct qlc_83xx_idc idc;
	struct qlc_83xx_fw_info fw_info;
	struct qlcnic_intrpt_config *intr_tbl;
	u32 *reg_tbl;
	u32 *ext_reg_tbl;
@@ -947,6 +955,7 @@ struct qlcnic_ipaddr {
#define QLCNIC_TEST_IN_PROGRESS		52
#define QLCNIC_UNDEFINED_ERROR		53
#define QLCNIC_LB_CABLE_NOT_CONN	54
#define QLCNIC_ILB_MAX_RCV_LOOP	10

struct qlcnic_filter {
	struct hlist_node fnode;
@@ -1470,6 +1479,7 @@ int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
/*  eSwitch management functions */
int qlcnic_config_switch_port(struct qlcnic_adapter *,
				struct qlcnic_esw_func_cfg *);

int qlcnic_get_eswitch_port_config(struct qlcnic_adapter *,
				struct qlcnic_esw_func_cfg *);
int qlcnic_config_port_mirroring(struct qlcnic_adapter *, u8, u8, u8);
@@ -1501,6 +1511,9 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *,
			    struct qlcnic_esw_func_cfg *);
void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *,
				      struct qlcnic_esw_func_cfg *);

void qlcnic_down(struct qlcnic_adapter *, struct net_device *);
int qlcnic_up(struct qlcnic_adapter *, struct net_device *);
void __qlcnic_down(struct qlcnic_adapter *, struct net_device *);
void qlcnic_detach(struct qlcnic_adapter *);
void qlcnic_teardown_intr(struct qlcnic_adapter *);
@@ -1508,6 +1521,7 @@ int qlcnic_attach(struct qlcnic_adapter *);
int __qlcnic_up(struct qlcnic_adapter *, struct net_device *);
void qlcnic_restore_indev_addr(struct net_device *, unsigned long);

int qlcnic_check_temp(struct qlcnic_adapter *);

/*
 * QLOGIC Board information
+208 −7
Original line number Diff line number Diff line
@@ -267,6 +267,8 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
static struct qlcnic_nic_template qlcnic_83xx_ops = {
	.config_bridged_mode	= qlcnic_config_bridged_mode,
	.config_led		= qlcnic_config_led,
	.request_reset          = qlcnic_83xx_idc_request_reset,
	.cancel_idc_work        = qlcnic_83xx_idc_exit,
	.napi_add		= qlcnic_83xx_napi_add,
	.napi_del		= qlcnic_83xx_napi_del,
	.config_ipaddr		= qlcnic_83xx_config_ipaddr,
@@ -589,6 +591,7 @@ int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter)
			adapter->ahw->port_type = QLCNIC_XGBE;
		else
			adapter->ahw->port_type = QLCNIC_GBE;

		if (QLC_83XX_AUTONEG(adapter->ahw->port_config))
			adapter->ahw->link_autoneg = AUTONEG_ENABLE;
	}
@@ -603,6 +606,7 @@ void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *adapter)
		val = BIT_2 | ((adapter->ahw->num_msix - 1) << 8);
	else
		val = BIT_2;

	QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val);
}

@@ -612,9 +616,7 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
	u32 op_mode, priv_level;
	struct qlcnic_hardware_context *ahw = adapter->ahw;

	/* Determine FW API version */
	ahw->fw_hal_version = 2;
	/* Find PCI function number */
	qlcnic_get_func_no(adapter);

	/* Determine function privilege level */
@@ -691,6 +693,13 @@ int qlcnic_83xx_mbx_op(struct qlcnic_adapter *adapter,
	struct qlcnic_hardware_context *ahw = adapter->ahw;

	opcode = LSW(cmd->req.arg[0]);
	if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) {
		dev_info(&adapter->pdev->dev,
			 "Mailbox cmd attempted, 0x%x\n", opcode);
		dev_info(&adapter->pdev->dev, "Mailbox detached\n");
		return 0;
	}

	spin_lock(&ahw->mbx_lock);
	mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL);

@@ -853,6 +862,7 @@ static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter,
{
	dev_dbg(&adapter->pdev->dev, "Completion AEN:0x%x.\n",
		QLCNIC_MBX_RSP(data[0]));
	clear_bit(QLC_83XX_IDC_COMP_AEN, &adapter->ahw->idc.status);
	return;
}

@@ -1306,7 +1316,7 @@ int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
{
	struct qlcnic_hardware_context *ahw = adapter->ahw;
	int status = 0;
	int status = 0, loop = 0;
	u32 config;

	status = qlcnic_83xx_get_port_config(adapter);
@@ -1314,6 +1324,7 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
		return status;

	config = ahw->port_config;
	set_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);

	if (mode == QLCNIC_ILB_MODE)
		ahw->port_config |= QLC_83XX_CFG_LOOPBACK_HSS;
@@ -1326,9 +1337,21 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
			"Failed to Set Loopback Mode = 0x%x.\n",
			ahw->port_config);
		ahw->port_config = config;
		clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
		return status;
	}

	/* Wait until firmware send IDC Completion AEN */
	do {
		msleep(300);
		if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
			dev_err(&adapter->pdev->dev,
				"FW did not generate IDC completion AEN\n");
			clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
			return -EIO;
		}
	} while (test_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status));

	qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0,
				  QLCNIC_MAC_ADD);
	return status;
@@ -1337,9 +1360,10 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
{
	struct qlcnic_hardware_context *ahw = adapter->ahw;
	int status = 0;
	int status = 0, loop = 0;
	u32 config = ahw->port_config;

	set_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
	if (mode == QLCNIC_ILB_MODE)
		ahw->port_config &= ~QLC_83XX_CFG_LOOPBACK_HSS;
	if (mode == QLCNIC_ELB_MODE)
@@ -1351,9 +1375,21 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
			"Failed to Clear Loopback Mode = 0x%x.\n",
			ahw->port_config);
		ahw->port_config = config;
		clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
		return status;
	}

	/* Wait until firmware send IDC Completion AEN */
	do {
		msleep(300);
		if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
			dev_err(&adapter->pdev->dev,
				"Firmware didn't sent IDC completion AEN\n");
			clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
			return -EIO;
		}
	} while (test_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status));

	qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0,
				  QLCNIC_MAC_DEL);
	return status;
@@ -1813,7 +1849,7 @@ void qlcnic_83xx_unlock_flash(struct qlcnic_adapter *adapter)
	QLC_SHARED_REG_WR32(adapter, QLCNIC_FLASH_LOCK_OWNER, 0xFF);
}

static int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *adapter,
int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *adapter,
				      u32 flash_addr, u8 *p_data,
				      int count)
{
@@ -2142,3 +2178,168 @@ int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *adapter, u32 addr,

	return 0;
}

static void qlcnic_83xx_recover_driver_lock(struct qlcnic_adapter *adapter)
{
	u32 val, id;

	val = QLCRDX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK);

	/* Check if recovery need to be performed by the calling function */
	if ((val & QLC_83XX_DRV_LOCK_RECOVERY_STATUS_MASK) == 0) {
		val = val & ~0x3F;
		val = val | ((adapter->portnum << 2) |
			     QLC_83XX_NEED_DRV_LOCK_RECOVERY);
		QLCWRX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK, val);
		dev_info(&adapter->pdev->dev,
			 "%s: lock recovery initiated\n", __func__);
		msleep(QLC_83XX_DRV_LOCK_RECOVERY_DELAY);
		val = QLCRDX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK);
		id = ((val >> 2) & 0xF);
		if (id == adapter->portnum) {
			val = val & ~QLC_83XX_DRV_LOCK_RECOVERY_STATUS_MASK;
			val = val | QLC_83XX_DRV_LOCK_RECOVERY_IN_PROGRESS;
			QLCWRX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK, val);
			/* Force release the lock */
			QLCRDX(adapter->ahw, QLC_83XX_DRV_UNLOCK);
			/* Clear recovery bits */
			val = val & ~0x3F;
			QLCWRX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK, val);
			dev_info(&adapter->pdev->dev,
				 "%s: lock recovery completed\n", __func__);
		} else {
			dev_info(&adapter->pdev->dev,
				 "%s: func %d to resume lock recovery process\n",
				 __func__, id);
		}
	} else {
		dev_info(&adapter->pdev->dev,
			 "%s: lock recovery initiated by other functions\n",
			 __func__);
	}
}

int qlcnic_83xx_lock_driver(struct qlcnic_adapter *adapter)
{
	u32 lock_alive_counter, val, id, i = 0, status = 0, temp = 0;
	int max_attempt = 0;

	while (status == 0) {
		status = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK);
		if (status)
			break;

		msleep(QLC_83XX_DRV_LOCK_WAIT_DELAY);
		i++;

		if (i == 1)
			temp = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID);

		if (i == QLC_83XX_DRV_LOCK_WAIT_COUNTER) {
			val = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID);
			if (val == temp) {
				id = val & 0xFF;
				dev_info(&adapter->pdev->dev,
					 "%s: lock to be recovered from %d\n",
					 __func__, id);
				qlcnic_83xx_recover_driver_lock(adapter);
				i = 0;
				max_attempt++;
			} else {
				dev_err(&adapter->pdev->dev,
					"%s: failed to get lock\n", __func__);
				return -EIO;
			}
		}

		/* Force exit from while loop after few attempts */
		if (max_attempt == QLC_83XX_MAX_DRV_LOCK_RECOVERY_ATTEMPT) {
			dev_err(&adapter->pdev->dev,
				"%s: failed to get lock\n", __func__);
			return -EIO;
		}
	}

	val = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID);
	lock_alive_counter = val >> 8;
	lock_alive_counter++;
	val = lock_alive_counter << 8 | adapter->portnum;
	QLCWRX(adapter->ahw, QLC_83XX_DRV_LOCK_ID, val);

	return 0;
}

void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *adapter)
{
	u32 val, lock_alive_counter, id;

	val = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID);
	id = val & 0xFF;
	lock_alive_counter = val >> 8;

	if (id != adapter->portnum)
		dev_err(&adapter->pdev->dev,
			"%s:Warning func %d is unlocking lock owned by %d\n",
			__func__, adapter->portnum, id);

	val = (lock_alive_counter << 8) | 0xFF;
	QLCWRX(adapter->ahw, QLC_83XX_DRV_LOCK_ID, val);
	QLCRDX(adapter->ahw, QLC_83XX_DRV_UNLOCK);
}

int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
				u32 *data, u32 count)
{
	int i, j, ret = 0;
	u32 temp;

	/* Check alignment */
	if (addr & 0xF)
		return -EIO;

	mutex_lock(&adapter->ahw->mem_lock);
	qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_HI, 0);

	for (i = 0; i < count; i++, addr += 16) {
		if (!((ADDR_IN_RANGE(addr, QLCNIC_ADDR_QDR_NET,
				     QLCNIC_ADDR_QDR_NET_MAX)) ||
		      (ADDR_IN_RANGE(addr, QLCNIC_ADDR_DDR_NET,
				     QLCNIC_ADDR_DDR_NET_MAX)))) {
			mutex_unlock(&adapter->ahw->mem_lock);
			return -EIO;
		}

		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_LO, addr);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_LO,
					     *data++);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_HI,
					     *data++);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_ULO,
					     *data++);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_UHI,
					     *data++);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
					     QLCNIC_TA_WRITE_ENABLE);
		qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
					     QLCNIC_TA_WRITE_START);

		for (j = 0; j < MAX_CTL_CHECK; j++) {
			temp = qlcnic_83xx_rd_reg_indirect(adapter,
							   QLCNIC_MS_CTRL);
			if ((temp & TA_CTL_BUSY) == 0)
				break;
		}

		/* Status check failure */
		if (j >= MAX_CTL_CHECK) {
			printk_ratelimited(KERN_WARNING
					   "MS memory write failed\n");
			mutex_unlock(&adapter->ahw->mem_lock);
			return -EIO;
		}
	}

	mutex_unlock(&adapter->ahw->mem_lock);

	return ret;
}
+80 −0
Original line number Diff line number Diff line
@@ -53,6 +53,30 @@
#define QLCNIC_HOST_RDS_MBX_IDX			88
#define QLCNIC_MAX_RING_SETS			8

/* Pause control registers */
#define QLC_83XX_SRE_SHIM_REG		0x0D200284
#define QLC_83XX_PORT0_THRESHOLD	0x0B2003A4
#define QLC_83XX_PORT1_THRESHOLD	0x0B2013A4
#define QLC_83XX_PORT0_TC_MC_REG	0x0B200388
#define QLC_83XX_PORT1_TC_MC_REG	0x0B201388
#define QLC_83XX_PORT0_TC_STATS		0x0B20039C
#define QLC_83XX_PORT1_TC_STATS		0x0B20139C
#define QLC_83XX_PORT2_IFB_THRESHOLD	0x0B200704
#define QLC_83XX_PORT3_IFB_THRESHOLD	0x0B201704

/* Peg PC status registers */
#define QLC_83XX_CRB_PEG_NET_0		0x3400003c
#define QLC_83XX_CRB_PEG_NET_1		0x3410003c
#define QLC_83XX_CRB_PEG_NET_2		0x3420003c
#define QLC_83XX_CRB_PEG_NET_3		0x3430003c
#define QLC_83XX_CRB_PEG_NET_4		0x34b0003c

/* Firmware image definitions */
#define QLC_83XX_BOOTLOADER_FLASH_ADDR	0x10000
#define QLC_83XX_FW_FILE_NAME		"83xx_fw.bin"
#define QLC_83XX_BOOT_FROM_FLASH	0
#define QLC_83XX_BOOT_FROM_FILE		0x12345678

struct qlcnic_intrpt_config {
	u8	type;
	u8	enabled;
@@ -65,6 +89,49 @@ struct qlcnic_macvlan_mbx {
	u16	vlan;
};

struct qlc_83xx_fw_info {
	const struct firmware	*fw;
	u16	major_fw_version;
	u8	minor_fw_version;
	u8	sub_fw_version;
	u8	fw_build_num;
	u8	load_from_file;
};

#define QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY		0x1
#define QLC_83XX_IDC_GRACEFULL_RESET			0x2
#define QLC_83XX_IDC_TIMESTAMP				0
#define QLC_83XX_IDC_DURATION				1
#define QLC_83XX_IDC_INIT_TIMEOUT_SECS			30
#define QLC_83XX_IDC_RESET_ACK_TIMEOUT_SECS		10
#define QLC_83XX_IDC_RESET_TIMEOUT_SECS		10
#define QLC_83XX_IDC_QUIESCE_ACK_TIMEOUT_SECS		20
#define QLC_83XX_IDC_FW_POLL_DELAY			(1 * HZ)
#define QLC_83XX_IDC_FW_FAIL_THRESH			2
#define QLC_83XX_IDC_MAX_FUNC_PER_PARTITION_INFO	8
#define QLC_83XX_IDC_MAX_CNA_FUNCTIONS			16
#define QLC_83XX_IDC_MAJOR_VERSION			1
#define QLC_83XX_IDC_MINOR_VERSION			0
#define QLC_83XX_IDC_FLASH_PARAM_ADDR			0x3e8020

/* Mailbox process AEN count */
#define QLC_83XX_MBX_AEN_CNT 5

struct qlcnic_adapter;
struct qlc_83xx_idc {
	int (*state_entry) (struct qlcnic_adapter *);
	u64		sec_counter;
	u64		delay;
	unsigned long	status;
	int		err_code;
	int		collect_dump;
	u8		curr_state;
	u8		prev_state;
	u8		vnic_state;
	u8		vnic_wait_limit;
	u8		quiesce_req;
	char		**name;
};

/* Mailbox process AEN count */
#define QLC_83XX_IDC_COMP_AEN			3
@@ -303,4 +370,17 @@ int qlcnic_83xx_save_flash_status(struct qlcnic_adapter *);
int qlcnic_83xx_restore_flash_status(struct qlcnic_adapter *, int);
int qlcnic_83xx_read_flash_mfg_id(struct qlcnic_adapter *);
int qlcnic_83xx_read_flash_descriptor_table(struct qlcnic_adapter *);
int qlcnic_83xx_flash_read32(struct qlcnic_adapter *, u32, u8 *, int);
int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *,
				      u32, u8 *, int);
int qlcnic_83xx_init(struct qlcnic_adapter *);
int qlcnic_83xx_idc_ready_state_entry(struct qlcnic_adapter *);
int qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev);
void qlcnic_83xx_idc_poll_dev_state(struct work_struct *);
void qlcnic_83xx_idc_exit(struct qlcnic_adapter *);
void qlcnic_83xx_idc_request_reset(struct qlcnic_adapter *, u32);
int qlcnic_83xx_lock_driver(struct qlcnic_adapter *);
void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *);
int qlcnic_83xx_set_default_offload_settings(struct qlcnic_adapter *);
int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *, u64, u32 *, u32);
#endif
+1513 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading