Skip to content

Commit

Permalink
qlcnic: 83xx CNA inter driver communication mechanism
Browse files Browse the repository at this point in the history
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: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Sony Chacko authored and David S. Miller committed Jan 2, 2013
1 parent d865ebb commit 629263a
Show file tree
Hide file tree
Showing 6 changed files with 1,830 additions and 19 deletions.
3 changes: 2 additions & 1 deletion drivers/net/ethernet/qlogic/qlcnic/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 14 additions & 0 deletions drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1501,13 +1511,17 @@ 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 *);
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
Expand Down
215 changes: 208 additions & 7 deletions drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}

Expand All @@ -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 */
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -1306,14 +1316,15 @@ 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);
if (status)
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;
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -1813,9 +1849,9 @@ 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,
u32 flash_addr, u8 *p_data,
int count)
int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *adapter,
u32 flash_addr, u8 *p_data,
int count)
{
int i, ret;
u32 word, range, flash_offset, addr = flash_addr;
Expand Down Expand Up @@ -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;
}
Loading

0 comments on commit 629263a

Please sign in to comment.