/*
* QLogic qlcnic NIC Driver
*/
#include "qlcnic_sriov.h"
#include "qlcnic.h"
#include "qlcnic_hw.h"
/* Reset template definitions */
#define QLC_83XX_RESTART_TEMPLATE_SIZE 0x2000
#define QLC_83XX_RESET_TEMPLATE_ADDR 0x4F0000
#define QLC_83XX_RESET_SEQ_VERSION 0x0101
#define QLC_83XX_OPCODE_NOP 0x0000
#define QLC_83XX_OPCODE_WRITE_LIST 0x0001
#define QLC_83XX_OPCODE_READ_WRITE_LIST 0x0002
#define QLC_83XX_OPCODE_POLL_LIST 0x0004
#define QLC_83XX_OPCODE_POLL_WRITE_LIST 0x0008
#define QLC_83XX_OPCODE_READ_MODIFY_WRITE 0x0010
#define QLC_83XX_OPCODE_SEQ_PAUSE 0x0020
#define QLC_83XX_OPCODE_SEQ_END 0x0040
#define QLC_83XX_OPCODE_TMPL_END 0x0080
#define QLC_83XX_OPCODE_POLL_READ_LIST 0x0100
/* EPORT control registers */
#define QLC_83XX_RESET_CONTROL 0x28084E50
#define QLC_83XX_RESET_REG 0x28084E60
#define QLC_83XX_RESET_PORT0 0x28084E70
#define QLC_83XX_RESET_PORT1 0x28084E80
#define QLC_83XX_RESET_PORT2 0x28084E90
#define QLC_83XX_RESET_PORT3 0x28084EA0
#define QLC_83XX_RESET_SRESHIM 0x28084EB0
#define QLC_83XX_RESET_EPGSHIM 0x28084EC0
#define QLC_83XX_RESET_ETHERPCS 0x28084ED0
static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter);
static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);
static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);
/* Template header */
struct qlc_83xx_reset_hdr {
#if defined(__LITTLE_ENDIAN)
u16 version;
u16 signature;
u16 size;
u16 entries;
u16 hdr_size;
u16 checksum;
u16 init_offset;
u16 start_offset;
#elif defined(__BIG_ENDIAN)
u16 signature;
u16 version;
u16 entries;
u16 size;
u16 checksum;
u16 hdr_size;
u16 start_offset;
u16 init_offset;
#endif
} __packed;
/* Command entry header. */
struct qlc_83xx_entry_hdr {
#if defined(__LITTLE_ENDIAN)
u16 cmd;
u16 size;
u16 count;
u16 delay;
#elif defined(__BIG_ENDIAN)
u16 size;
u16 cmd;
u16 delay;
u16 count;
#endif
} __packed;
/* Generic poll command */
struct qlc_83xx_poll {
u32 mask;
u32 status;
} __packed;
/* Read modify write command */
struct qlc_83xx_rmw {
u32 mask;
u32 xor_value;
u32 or_value;
#if defined(__LITTLE_ENDIAN)
u8 shl;
u8 shr;
u8 index_a;
u8 rsvd;
#elif defined(__BIG_ENDIAN)
u8 rsvd;
u8 index_a;
u8 shr;
u8 shl;
#endif
} __packed;
/* Generic command with 2 DWORD */
struct qlc_83xx_entry {
u32 arg1;
u32 arg2;
} __packed;
/* Generic command with 4 DWORD */
struct qlc_83xx_quad_entry {
u32 dr_addr;
u32 dr_value;
u32 ar_addr;
u32 ar_value;
} __packed;
static const char *const qlc_83xx_idc_states[] = {
"Unknown",
"Cold",
"Init",
"Ready",
"Need Reset",
"Need Quiesce",
"Failed",
"Quiesce"
};
static int
qlcnic_83xx_idc_check_driver_presence_reg(struct qlcnic_adapter *adapter)
{
u32 val;
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE);
if ((val & 0xFFFF))
return 1;
else
return 0;
}
static void qlcnic_83xx_idc_log_state_history(struct qlcnic_adapter *adapter)
{
u32 cur, prev;
cur = adapter->ahw->idc.curr_state;
prev = adapter->ahw->idc.prev_state;
dev_info(&adapter->pdev->dev,
"current state = %s, prev state = %s\n",
adapter->ahw->idc.name[cur],
adapter->ahw->idc.name[prev]);
}
static int qlcnic_83xx_idc_update_audit_reg(struct qlcnic_adapter *adapter,
u8 mode, int lock)
{
u32 val;
int seconds;
if (lock) {
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
}
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT);
val |= (adapter->portnum & 0xf);
val |= mode << 7;
if (mode)
seconds = jiffies / HZ - adapter->ahw->idc.sec_counter;
else
seconds = jiffies / HZ;
val |= seconds << 8;
QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT, val);
adapter->ahw->idc.sec_counter = jiffies / HZ;
if (lock)
qlcnic_83xx_unlock_driver(adapter);
return 0;
}
static void qlcnic_83xx_idc_update_minor_version(struct qlcnic_adapter *adapter)
{
u32 val;
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_MIN_VERSION);
val = val & ~(0x3 << (adapter->portnum * 2));
val = val | (QLC_83XX_IDC_MINOR_VERSION << (adapter->portnum * 2));
QLCWRX(adapter->ahw, QLC_83XX_IDC_MIN_VERSION, val);
}
static int qlcnic_83xx_idc_update_major_version(struct qlcnic_adapter *adapter,
int lock)
{
u32 val;
if (lock) {
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
}
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_MAJ_VERSION);
val = val & ~0xFF;
val = val | QLC_83XX_IDC_MAJOR_VERSION;
QLCWRX(adapter->ahw, QLC_83XX_IDC_MAJ_VERSION, val);
if (lock)
qlcnic_83xx_unlock_driver(adapter);
return 0;
}
static int
qlcnic_83xx_idc_update_drv_presence_reg(struct qlcnic_adapter *adapter,
int status, int lock)
{
u32 val;
if (lock) {
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
}
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE);
if (status)
val = val | (1 << adapter->portnum);
else
val = val & ~(1 << adapter->portnum);
QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE, val);
qlcnic_83xx_idc_update_minor_version(adapter);
if (lock)
qlcnic_83xx_unlock_driver(adapter);
return 0;
}
static int qlcnic_83xx_idc_check_major_version(struct qlcnic_adapter *adapter)
{
u32 val;
u8 version;
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_MAJ_VERSION);
version = val & 0xFF;
if (version != QLC_83XX_IDC_MAJOR_VERSION) {
dev_info(&adapter->pdev->dev,
"%s:mismatch. version 0x%x, expected version 0x%x\n",
__func__, version, QLC_83XX_IDC_MAJOR_VERSION);
return -EIO;
}
return 0;
}
static int qlcnic_83xx_idc_clear_registers(struct qlcnic_adapter *adapter,
int lock)
{
u32 val;
if (lock) {
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
}
QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_ACK, 0);
/* Clear gracefull reset bit */
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL);
val &= ~QLC_83XX_IDC_GRACEFULL_RESET;
QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val);
if (lock)
qlcnic_83xx_unlock_driver(adapter);
return 0;
}
static int qlcnic_83xx_idc_update_drv_ack_reg(struct qlcnic_adapter *adapter,
int flag, int lock)
{
u32 val;
if (lock) {
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
}
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_ACK);
if (flag)
val = val | (1 << adapter->portnum);
else
val = val & ~(1 << adapter->portnum);
QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_ACK, val);
if (lock)
qlcnic_83xx_unlock_driver(adapter);
return 0;
}
static int qlcnic_83xx_idc_check_timeout(struct qlcnic_adapter *adapter,
int time_limit)
{
u64 seconds;
seconds = jiffies / HZ - adapter->ahw->idc.sec_counter;
if (seconds <= time_limit)
return 0;
else
return -EBUSY;
}
/**
* qlcnic_83xx_idc_check_reset_ack_reg
*
* @adapter: adapter structure
*
* Check ACK wait limit and clear the functions which failed to ACK
*
* Return 0 if all functions have acknowledged the reset request.
**/
static int qlcnic_83xx_idc_check_reset_ack_reg(struct qlcnic_adapter *adapter)
{
int timeout;
u32 ack, presence, val;
timeout = QLC_83XX_IDC_RESET_TIMEOUT_SECS;
ack = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_ACK);
presence = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE);
dev_info(&adapter->pdev->dev,
"%s: ack = 0x%x, presence = 0x%x\n", __func__, ack, presence);
if (!((ack & presence) == presence)) {
if (qlcnic_83xx_idc_check_timeout(adapter, timeout)) {
/* Clear functions which failed to ACK */
dev_info(&adapter->pdev->dev,
"%s: ACK wait exceeds time limit\n", __func__);
val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE);
val = val & ~(ack ^ presence);
if (qlcnic_83xx_lock_driver(adapter))
return -EBUSY;
QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE, val);
dev_info(&adapter->pdev->dev,
"%s: updated drv presence reg = 0x%x\n",
__func__, val);
qlcnic_83xx_unlock_driver(adapter);
return 0;
} else {
return 1;
}
} else {
dev_info(&adapter->pdev->dev,
"%s: Reset ACK received from all functions\n",
__func__);
return 0;
}
}
/**
* qlcnic_83xx_idc_tx_soft_reset
*
* @adapter