Commit 5d6fa099 authored by David S. Miller's avatar David S. Miller

Merge branch 'qlcnic'

Himanshu Madhani says:

====================
qlcnic: ethtool enhancements and code cleanup.

This patch series contains

o updates to ethtool for pause settings and enhance
  register dump to display mask and ring indices.
o cleanup in DCB code and remove redundant eSwitch enablement command.
o fixed firmware dump collection logic to skip unknown entries.

Changes from v3 -> v4
o Dropped patch for Tx queue validation to be submitted in net.

Changes from v2 -> v3

o Updated patch to print informational messages as per Joe Perches's comment.

Changes from v1 -> v2

o Dropped patch to register device if adapter is in FAILED state for more rework.
o Updated patch to display ring indices via ethtool per Ben Hutchings's comment.
o Update patch for DCB cleanup per Stephen Hemminger's comment.

Please apply to net-next.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 28be6e07 60b4a1f3
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
#define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3 #define _QLCNIC_LINUX_MINOR 3
#define _QLCNIC_LINUX_SUBVERSION 50 #define _QLCNIC_LINUX_SUBVERSION 51
#define QLCNIC_LINUX_VERSIONID "5.3.50" #define QLCNIC_LINUX_VERSIONID "5.3.51"
#define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
...@@ -961,8 +961,6 @@ struct qlcnic_ipaddr { ...@@ -961,8 +961,6 @@ struct qlcnic_ipaddr {
#define __QLCNIC_SRIOV_CAPABLE 11 #define __QLCNIC_SRIOV_CAPABLE 11
#define __QLCNIC_MBX_POLL_ENABLE 12 #define __QLCNIC_MBX_POLL_ENABLE 12
#define __QLCNIC_DIAG_MODE 13 #define __QLCNIC_DIAG_MODE 13
#define __QLCNIC_DCB_STATE 14
#define __QLCNIC_DCB_IN_AEN 15
#define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2 #define QLCNIC_LOOPBACK_TEST 2
...@@ -1199,6 +1197,7 @@ struct qlcnic_npar_info { ...@@ -1199,6 +1197,7 @@ struct qlcnic_npar_info {
u8 promisc_mode; u8 promisc_mode;
u8 offload_flags; u8 offload_flags;
u8 pci_func; u8 pci_func;
u8 mac[ETH_ALEN];
}; };
struct qlcnic_eswitch { struct qlcnic_eswitch {
...@@ -2115,98 +2114,4 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter) ...@@ -2115,98 +2114,4 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter)
return status; return status;
} }
static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->get_hw_capability)
return dcb->ops->get_hw_capability(adapter);
return 0;
}
static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->free)
dcb->ops->free(adapter);
}
static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->attach)
return dcb->ops->attach(adapter);
return 0;
}
static inline int
qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->query_hw_capability)
return dcb->ops->query_hw_capability(adapter, buf);
return 0;
}
static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->get_info)
dcb->ops->get_info(adapter);
}
static inline int
qlcnic_dcb_query_cee_param(struct qlcnic_adapter *adapter, char *buf, u8 type)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->query_cee_param)
return dcb->ops->query_cee_param(adapter, buf, type);
return 0;
}
static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->get_cee_cfg)
return dcb->ops->get_cee_cfg(adapter);
return 0;
}
static inline void
qlcnic_dcb_register_aen(struct qlcnic_adapter *adapter, u8 flag)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->register_aen)
dcb->ops->register_aen(adapter, flag);
}
static inline void qlcnic_dcb_handle_aen(struct qlcnic_adapter *adapter,
void *msg)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->handle_aen)
dcb->ops->handle_aen(adapter, msg);
}
static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb = adapter->dcb;
if (dcb && dcb->ops->init_dcbnl_ops)
dcb->ops->init_dcbnl_ops(adapter);
}
#endif /* __QLCNIC_H_ */ #endif /* __QLCNIC_H_ */
...@@ -902,7 +902,7 @@ void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) ...@@ -902,7 +902,7 @@ void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
QLCNIC_MBX_RSP(event[0])); QLCNIC_MBX_RSP(event[0]));
break; break;
case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT: case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT:
qlcnic_dcb_handle_aen(adapter, (void *)&event[1]); qlcnic_dcb_aen_handler(adapter->dcb, (void *)&event[1]);
break; break;
default: default:
dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n", dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n",
...@@ -2321,19 +2321,7 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, ...@@ -2321,19 +2321,7 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
i++; i++;
memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2); memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2);
i = i + 3; i = i + 3;
if (ahw->op_mode == QLCNIC_MGMT_FUNC)
dev_info(dev, "id = %d active = %d type = %d\n"
"\tport = %d min bw = %d max bw = %d\n"
"\tmac_addr = %pM\n", pci_info->id,
pci_info->active, pci_info->type,
pci_info->default_port,
pci_info->tx_min_bw,
pci_info->tx_max_bw, pci_info->mac);
} }
if (ahw->op_mode == QLCNIC_MGMT_FUNC)
dev_info(dev, "Max functions = %d, active functions = %d\n",
ahw->max_pci_func, ahw->act_pci_func);
} else { } else {
dev_err(dev, "Failed to get PCI Info, error = %d\n", err); dev_err(dev, "Failed to get PCI Info, error = %d\n", err);
err = -EIO; err = -EIO;
...@@ -3279,12 +3267,12 @@ int qlcnic_83xx_reg_test(struct qlcnic_adapter *adapter) ...@@ -3279,12 +3267,12 @@ int qlcnic_83xx_reg_test(struct qlcnic_adapter *adapter)
return 0; return 0;
} }
int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter) inline int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter)
{ {
return (ARRAY_SIZE(qlcnic_83xx_ext_reg_tbl) * return (ARRAY_SIZE(qlcnic_83xx_ext_reg_tbl) *
sizeof(adapter->ahw->ext_reg_tbl)) + sizeof(*adapter->ahw->ext_reg_tbl)) +
(ARRAY_SIZE(qlcnic_83xx_reg_tbl) + (ARRAY_SIZE(qlcnic_83xx_reg_tbl) *
sizeof(adapter->ahw->reg_tbl)); sizeof(*adapter->ahw->reg_tbl));
} }
int qlcnic_83xx_get_registers(struct qlcnic_adapter *adapter, u32 *regs_buff) int qlcnic_83xx_get_registers(struct qlcnic_adapter *adapter, u32 *regs_buff)
...@@ -3381,10 +3369,21 @@ void qlcnic_83xx_get_pauseparam(struct qlcnic_adapter *adapter, ...@@ -3381,10 +3369,21 @@ void qlcnic_83xx_get_pauseparam(struct qlcnic_adapter *adapter,
} }
config = ahw->port_config; config = ahw->port_config;
if (config & QLC_83XX_CFG_STD_PAUSE) { if (config & QLC_83XX_CFG_STD_PAUSE) {
if (config & QLC_83XX_CFG_STD_TX_PAUSE) switch (MSW(config)) {
case QLC_83XX_TX_PAUSE:
pause->tx_pause = 1; pause->tx_pause = 1;
if (config & QLC_83XX_CFG_STD_RX_PAUSE) break;
case QLC_83XX_RX_PAUSE:
pause->rx_pause = 1; pause->rx_pause = 1;
break;
case QLC_83XX_TX_RX_PAUSE:
default:
/* Backward compatibility for existing
* flash definitions
*/
pause->tx_pause = 1;
pause->rx_pause = 1;
}
} }
if (QLC_83XX_AUTONEG(config)) if (QLC_83XX_AUTONEG(config))
...@@ -3427,7 +3426,8 @@ int qlcnic_83xx_set_pauseparam(struct qlcnic_adapter *adapter, ...@@ -3427,7 +3426,8 @@ int qlcnic_83xx_set_pauseparam(struct qlcnic_adapter *adapter,
ahw->port_config &= ~QLC_83XX_CFG_STD_RX_PAUSE; ahw->port_config &= ~QLC_83XX_CFG_STD_RX_PAUSE;
ahw->port_config |= QLC_83XX_CFG_STD_TX_PAUSE; ahw->port_config |= QLC_83XX_CFG_STD_TX_PAUSE;
} else if (!pause->rx_pause && !pause->tx_pause) { } else if (!pause->rx_pause && !pause->tx_pause) {
ahw->port_config &= ~QLC_83XX_CFG_STD_TX_RX_PAUSE; ahw->port_config &= ~(QLC_83XX_CFG_STD_TX_RX_PAUSE |
QLC_83XX_CFG_STD_PAUSE);
} }
status = qlcnic_83xx_set_port_config(adapter); status = qlcnic_83xx_set_port_config(adapter);
if (status) { if (status) {
......
...@@ -363,6 +363,9 @@ enum qlcnic_83xx_states { ...@@ -363,6 +363,9 @@ enum qlcnic_83xx_states {
#define QLC_83XX_LINK_EEE(data) ((data) & BIT_13) #define QLC_83XX_LINK_EEE(data) ((data) & BIT_13)
#define QLC_83XX_DCBX(data) (((data) >> 28) & 7) #define QLC_83XX_DCBX(data) (((data) >> 28) & 7)
#define QLC_83XX_AUTONEG(data) ((data) & BIT_15) #define QLC_83XX_AUTONEG(data) ((data) & BIT_15)
#define QLC_83XX_TX_PAUSE 0x10
#define QLC_83XX_RX_PAUSE 0x20
#define QLC_83XX_TX_RX_PAUSE 0x30
#define QLC_83XX_CFG_STD_PAUSE (1 << 5) #define QLC_83XX_CFG_STD_PAUSE (1 << 5)
#define QLC_83XX_CFG_STD_TX_PAUSE (1 << 20) #define QLC_83XX_CFG_STD_TX_PAUSE (1 << 20)
#define QLC_83XX_CFG_STD_RX_PAUSE (2 << 20) #define QLC_83XX_CFG_STD_RX_PAUSE (2 << 20)
...@@ -626,7 +629,7 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *); ...@@ -626,7 +629,7 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *);
int qlcnic_83xx_get_vnic_vport_info(struct qlcnic_adapter *, int qlcnic_83xx_get_vnic_vport_info(struct qlcnic_adapter *,
struct qlcnic_info *, u8); struct qlcnic_info *, u8);
int qlcnic_83xx_get_vnic_pf_info(struct qlcnic_adapter *, struct qlcnic_info *); int qlcnic_83xx_get_vnic_pf_info(struct qlcnic_adapter *, struct qlcnic_info *);
int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *, int); int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *, int, int *);
void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *); void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *);
void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data); void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data);
......
...@@ -636,7 +636,7 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) ...@@ -636,7 +636,7 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
if (adapter->portnum == 0) if (adapter->portnum == 0)
qlcnic_set_drv_version(adapter); qlcnic_set_drv_version(adapter);
qlcnic_dcb_get_info(adapter); qlcnic_dcb_get_info(adapter->dcb);
qlcnic_83xx_idc_attach_driver(adapter); qlcnic_83xx_idc_attach_driver(adapter);
return 0; return 0;
...@@ -818,6 +818,7 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter) ...@@ -818,6 +818,7 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter)
struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_mailbox *mbx = ahw->mailbox; struct qlcnic_mailbox *mbx = ahw->mailbox;
int ret = 0; int ret = 0;
u32 owner;
u32 val; u32 val;
/* Perform NIC configuration based ready state entry actions */ /* Perform NIC configuration based ready state entry actions */
...@@ -846,6 +847,10 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter) ...@@ -846,6 +847,10 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter)
clear_bit(QLC_83XX_MBX_READY, &mbx->status); clear_bit(QLC_83XX_MBX_READY, &mbx->status);
set_bit(__QLCNIC_RESETTING, &adapter->state); set_bit(__QLCNIC_RESETTING, &adapter->state);
qlcnic_83xx_idc_enter_need_reset_state(adapter, 1); qlcnic_83xx_idc_enter_need_reset_state(adapter, 1);
} else {
owner = qlcnic_83xx_idc_find_reset_owner_id(adapter);
if (ahw->pci_func == owner)
qlcnic_dump_fw(adapter);
} }
return -EIO; return -EIO;
} }
...@@ -1058,6 +1063,12 @@ void qlcnic_83xx_idc_poll_dev_state(struct work_struct *work) ...@@ -1058,6 +1063,12 @@ void qlcnic_83xx_idc_poll_dev_state(struct work_struct *work)
adapter->ahw->idc.prev_state = adapter->ahw->idc.curr_state; adapter->ahw->idc.prev_state = adapter->ahw->idc.curr_state;
qlcnic_83xx_periodic_tasks(adapter); qlcnic_83xx_periodic_tasks(adapter);
/* Do not reschedule if firmaware is in hanged state and auto
* recovery is disabled
*/
if ((adapter->flags & QLCNIC_FW_HANG) && !qlcnic_auto_fw_reset)
return;
/* Re-schedule the function */ /* Re-schedule the function */
if (test_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status)) if (test_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status))
qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state, qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state,
...@@ -2163,6 +2174,7 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter) ...@@ -2163,6 +2174,7 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter)
int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
{ {
struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_dcb *dcb;
int err = 0; int err = 0;
ahw->msix_supported = !!qlcnic_use_msi_x; ahw->msix_supported = !!qlcnic_use_msi_x;
...@@ -2220,8 +2232,10 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) ...@@ -2220,8 +2232,10 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
if (err) if (err)
goto disable_mbx_intr; goto disable_mbx_intr;
if (adapter->dcb && qlcnic_dcb_attach(adapter)) dcb = adapter->dcb;
qlcnic_clear_dcb_ops(adapter);
if (dcb && qlcnic_dcb_attach(dcb))
qlcnic_clear_dcb_ops(dcb);
/* Periodically monitor device status */ /* Periodically monitor device status */
qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work); qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work);
......
...@@ -94,13 +94,29 @@ qlcnic_83xx_config_vnic_buff_descriptors(struct qlcnic_adapter *adapter) ...@@ -94,13 +94,29 @@ qlcnic_83xx_config_vnic_buff_descriptors(struct qlcnic_adapter *adapter)
**/ **/
static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter) static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter)
{ {
int err = -EIO; struct qlcnic_hardware_context *ahw = adapter->ahw;
struct device *dev = &adapter->pdev->dev;
struct qlcnic_npar_info *npar;
int i, err = -EIO;
qlcnic_83xx_get_minidump_template(adapter); qlcnic_83xx_get_minidump_template(adapter);
if (!(adapter->flags & QLCNIC_ADAPTER_INITIALIZED)) { if (!(adapter->flags & QLCNIC_ADAPTER_INITIALIZED)) {
if (qlcnic_init_pci_info(adapter)) if (qlcnic_init_pci_info(adapter))
return err; return err;
npar = adapter->npars;
for (i = 0; i < ahw->act_pci_func; i++, npar++) {
dev_info(dev, "id:%d active:%d type:%d port:%d min_bw:%d max_bw:%d mac_addr:%pM\n",
npar->pci_func, npar->active, npar->type,
npar->phy_port, npar->min_bw, npar->max_bw,
npar->mac);
}
dev_info(dev, "Max functions = %d, active functions = %d\n",
ahw->max_pci_func, ahw->act_pci_func);
if (qlcnic_83xx_set_vnic_opmode(adapter)) if (qlcnic_83xx_set_vnic_opmode(adapter))
return err; return err;
...@@ -115,12 +131,12 @@ static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter) ...@@ -115,12 +131,12 @@ static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter)
return err; return err;
qlcnic_83xx_config_vnic_buff_descriptors(adapter); qlcnic_83xx_config_vnic_buff_descriptors(adapter);
adapter->ahw->msix_supported = !!qlcnic_use_msi_x; ahw->msix_supported = qlcnic_use_msi_x ? 1 : 0;
adapter->flags |= QLCNIC_ADAPTER_INITIALIZED; adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;
qlcnic_83xx_enable_vnic_mode(adapter, 1); qlcnic_83xx_enable_vnic_mode(adapter, 1);
dev_info(&adapter->pdev->dev, "HAL Version: %d, Management function\n", dev_info(dev, "HAL Version: %d, Management function\n",
adapter->ahw->fw_hal_version); ahw->fw_hal_version);
return 0; return 0;
} }
...@@ -240,8 +256,8 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter) ...@@ -240,8 +256,8 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter)
return 0; return 0;
} }
static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter, int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *adapter,
int func, int *port_id) int func, int *port_id)
{ {
struct qlcnic_info nic_info; struct qlcnic_info nic_info;
int err = 0; int err = 0;
...@@ -257,23 +273,8 @@ static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter, ...@@ -257,23 +273,8 @@ static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter,
else else
err = -EIO; err = -EIO;
return err; if (!err)
} adapter->eswitch[*port_id].flags |= QLCNIC_SWITCH_ENABLE;
int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *adapter, int func)
{
int id, err = 0;
err = qlcnic_83xx_get_eswitch_port_info(adapter, func, &id);
if (err)
return err;
if (!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) {
if (!qlcnic_enable_eswitch(adapter, id, 1))
adapter->eswitch[id].flags |= QLCNIC_SWITCH_ENABLE;
else
err = -EIO;
}
return err; return err;
} }
...@@ -8,26 +8,29 @@ ...@@ -8,26 +8,29 @@
#ifndef __QLCNIC_DCBX_H #ifndef __QLCNIC_DCBX_H
#define __QLCNIC_DCBX_H #define __QLCNIC_DCBX_H
void qlcnic_clear_dcb_ops(struct qlcnic_adapter *); #define QLCNIC_DCB_STATE 0
#define QLCNIC_DCB_AEN_MODE 1
#ifdef CONFIG_QLCNIC_DCB #ifdef CONFIG_QLCNIC_DCB
int __qlcnic_register_dcb(struct qlcnic_adapter *); int qlcnic_register_dcb(struct qlcnic_adapter *);
#else #else
static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) static inline int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
{ return 0; } { return 0; }
#endif #endif
struct qlcnic_dcb;
struct qlcnic_dcb_ops { struct qlcnic_dcb_ops {
void (*init_dcbnl_ops) (struct qlcnic_adapter *); int (*query_hw_capability) (struct qlcnic_dcb *, char *);
void (*free) (struct qlcnic_adapter *); int (*get_hw_capability) (struct qlcnic_dcb *);
int (*attach) (struct qlcnic_adapter *); int (*query_cee_param) (struct qlcnic_dcb *, char *, u8);
int (*query_hw_capability) (struct qlcnic_adapter *, char *); void (*init_dcbnl_ops) (struct qlcnic_dcb *);
int (*get_hw_capability) (struct qlcnic_adapter *); int (*register_aen) (struct qlcnic_dcb *, bool);
void (*get_info) (struct qlcnic_adapter *); void (*aen_handler) (struct qlcnic_dcb *, void *);
int (*query_cee_param) (struct qlcnic_adapter *, char *, u8); int (*get_cee_cfg) (struct qlcnic_dcb *);
int (*get_cee_cfg) (struct qlcnic_adapter *); void (*get_info) (struct qlcnic_dcb *);
int (*register_aen) (struct qlcnic_adapter *, bool); int (*attach) (struct qlcnic_dcb *);
void (*handle_aen) (struct qlcnic_adapter *, void *); void (*free) (struct qlcnic_dcb *);
}; };
struct qlcnic_dcb { struct qlcnic_dcb {
...@@ -37,5 +40,85 @@ struct qlcnic_dcb { ...@@ -37,5 +40,85 @@ struct qlcnic_dcb {
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct qlcnic_dcb_ops *ops; struct qlcnic_dcb_ops *ops;
struct qlcnic_dcb_cfg *cfg; struct qlcnic_dcb_cfg *cfg;
unsigned long state;
}; };
static inline void qlcnic_clear_dcb_ops(struct qlcnic_dcb *dcb)
{
kfree(dcb);
dcb = NULL;
}
static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->get_hw_capability)
return dcb->ops->get_hw_capability(dcb);
return 0;
}
static inline void qlcnic_dcb_free(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->free)
dcb->ops->free(dcb);
}
static inline int qlcnic_dcb_attach(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->attach)
return dcb->ops->attach(dcb);
return 0;
}
static inline int
qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *dcb, char *buf)
{
if (dcb && dcb->ops->query_hw_capability)
return dcb->ops->query_hw_capability(dcb, buf);
return 0;
}
static inline void qlcnic_dcb_get_info(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->get_info)
dcb->ops->get_info(dcb);
}
static inline int
qlcnic_dcb_query_cee_param(struct qlcnic_dcb *dcb, char *buf, u8 type)
{
if (dcb && dcb->ops->query_cee_param)
return dcb->ops->query_cee_param(dcb, buf, type);
return 0;
}
static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->get_cee_cfg)
return dcb->ops->get_cee_cfg(dcb);
return 0;
}
static inline void
qlcnic_dcb_register_aen(struct qlcnic_dcb *dcb, u8 flag)
{
if (dcb && dcb->ops->register_aen)
dcb->ops->register_aen(dcb, flag);
}
static inline void qlcnic_dcb_aen_handler(struct qlcnic_dcb *dcb, void *msg)
{
if (dcb && dcb->ops->aen_handler)
dcb->ops->aen_handler(dcb, msg);
}
static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->init_dcbnl_ops)
dcb->ops->init_dcbnl_ops(dcb);
}
#endif #endif
...@@ -187,8 +187,8 @@ static int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter) ...@@ -187,8 +187,8 @@ static int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter)
return -1; return -1;
} }
#define QLCNIC_RING_REGS_COUNT 20 #define QLCNIC_TX_INTR_NOT_CONFIGURED 0X78563412
#define QLCNIC_RING_REGS_LEN (QLCNIC_RING_REGS_COUNT * sizeof(u32))
#define QLCNIC_MAX_EEPROM_LEN 1024 #define QLCNIC_MAX_EEPROM_LEN 1024
static const u32 diag_registers[] = { static const u32 diag_registers[] = {
...@@ -219,7 +219,15 @@ static const u32 ext_diag_registers[] = { ...@@ -219,7 +219,15 @@ static const u32 ext_diag_registers[] = {
}; };
#define QLCNIC_MGMT_API_VERSION 2 #define QLCNIC_MGMT_API_VERSION 2
#define QLCNIC_ETHTOOL_REGS_VER 3 #define QLCNIC_ETHTOOL_REGS_VER 4
static inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter)
{
int ring_regs_cnt = (adapter->max_drv_tx_rings * 5) +
(adapter->max_rds_rings * 2) +
(adapter->max_sds_rings * 3) + 5;
return ring_regs_cnt * sizeof(u32);
}
static int qlcnic_get_regs_len(struct net_device *dev) static int qlcnic_get_regs_len(struct net_device *dev)
{ {
...@@ -231,7 +239,9 @@ static int qlcnic_get_regs_len(struct net_device *dev) ...@@ -231,7 +239,9 @@ static int qlcnic_get_regs_len(struct net_device *dev)
else else
len = sizeof(ext_diag_registers) + sizeof(diag_registers); len = sizeof(ext_diag_registers) + sizeof(diag_registers);
return QLCNIC_RING_REGS_LEN + len + QLCNIC_DEV_INFO_SIZE + 1; len += ((QLCNIC_DEV_INFO_SIZE + 2) * sizeof(u32));
len += qlcnic_get_ring_regs_len(adapter);
return len;
} }
static int qlcnic_get_eeprom_len(struct net_device *dev) static int qlcnic_get_eeprom_len(struct net_device *dev)
...@@ -493,6 +503,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) ...@@ -493,6 +503,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
struct qlcnic_adapter *adapter = netdev_priv(dev); struct qlcnic_adapter *adapter = netdev_priv(dev);
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
struct qlcnic_host_sds_ring *sds_ring; struct qlcnic_host_sds_ring *sds_ring;
struct qlcnic_host_rds_ring *rds_rings;
struct qlcnic_host_tx_ring *tx_ring;
u32 *regs_buff = p; u32 *regs_buff = p;
int ring, i = 0; int ring, i = 0;
...@@ -512,21 +524,35 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) ...@@ -512,21 +524,35 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) if (!test_bit(__QLCNIC_DEV_UP, &adapter->state))
return; return;
regs_buff[i++] = 0xFFEFCDAB; /* Marker btw regs and ring count*/ /* Marker btw regs and TX ring count */
regs_buff[i++] = 0xFFEFCDAB;
regs_buff[i++] = 1; /* No. of tx ring */
regs_buff[i++] = le32_to_cpu(*(adapter->tx_ring->hw_consumer)); regs_buff[i++] = adapter->max_drv_tx_rings; /* No. of TX ring */
regs_buff[i++] = readl(adapter->tx_ring->crb_cmd_producer); for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
regs_buff[i++] = 2; /* No. of rx ring */ regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer));
regs_buff[i++] = readl(recv_ctx->rds_rings[0].crb_rcv_producer); regs_buff[i++] = tx_ring->sw_consumer;
regs_buff[i++] = readl(recv_ctx->rds_rings[1].crb_rcv_producer); regs_buff[i++] = readl(tx_ring->crb_cmd_producer);
regs_buff[i++] = tx_ring->producer;
if (tx_ring->crb_intr_mask)
regs_buff[i++] = readl(tx_ring->crb_intr_mask);
else
regs_buff[i++] = QLCNIC_TX_INTR_NOT_CONFIGURED;
}
regs_buff[i++] = adapter->max_sds_rings; regs_buff[i++] = adapter->max_rds_rings; /* No. of RX ring */
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
rds_rings = &recv_ctx->rds_rings[ring];
regs_buff[i++] = readl(rds_rings->crb_rcv_producer);
regs_buff[i++] = rds_rings->producer;
}
regs_buff[i++] = adapter->max_sds_rings; /* No. of SDS ring */
for (ring = 0; ring < adapter->max_sds_rings; ring++) { for (ring = 0; ring < adapter->max_sds_rings; ring++) {
sds_ring = &(recv_ctx->sds_rings[ring]); sds_ring = &(recv_ctx->sds_rings[ring]);
regs_buff[i++] = readl(sds_ring->crb_sts_consumer); regs_buff[i++] = readl(sds_ring->crb_sts_consumer);
regs_buff[i++] = sds_ring->consumer;
regs_buff[i++] = readl(sds_ring->crb_intr_mask);
} }
} }
......
...@@ -1011,7 +1011,7 @@ static void qlcnic_handle_fw_message(int desc_cnt, int index, ...@@ -1011,7 +1011,7 @@ static void qlcnic_handle_fw_message(int desc_cnt, int index,
} }
break; break;
case QLCNIC_C2H_OPCODE_GET_DCB_AEN: case QLCNIC_C2H_OPCODE_GET_DCB_AEN:
qlcnic_dcb_handle_aen(adapter, (void *)&msg); qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg);
break; break;
default: default:
break; break;
......
...@@ -819,7 +819,7 @@ static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter) ...@@ -819,7 +819,7 @@ static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter)
int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
{ {
struct qlcnic_pci_info *pci_info; struct qlcnic_pci_info *pci_info;
int i, ret = 0, j = 0; int i, id = 0, ret = 0, j = 0;
u16 act_pci_func; u16 act_pci_func;
u8 pfn; u8 pfn;
...@@ -860,7 +860,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) ...@@ -860,7 +860,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
continue; continue;
if (qlcnic_port_eswitch_cfg_capability(adapter)) { if (qlcnic_port_eswitch_cfg_capability(adapter)) {
if (!qlcnic_83xx_enable_port_eswitch(adapter, pfn)) if (!qlcnic_83xx_set_port_eswitch_status(adapter, pfn,
&id))
adapter->npars[j].eswitch_status = true; adapter->npars[j].eswitch_status = true;
else else
continue; continue;
...@@ -875,15 +876,16 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) ...@@ -875,15 +876,16 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
adapter->npars[j].min_bw = pci_info[i].tx_min_bw; adapter->npars[j].min_bw = pci_info[i].tx_min_bw;
adapter->npars[j].max_bw = pci_info[i].tx_max_bw; adapter->npars[j].max_bw = pci_info[i].tx_max_bw;
memcpy(&adapter->npars[j].mac, &pci_info[i].mac, ETH_ALEN);
j++; j++;
} }
if (qlcnic_82xx_check(adapter)) { /* Update eSwitch status for adapters without per port eSwitch
* configuration capability
*/
if (!qlcnic_port_eswitch_cfg_capability(adapter)) {
for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++) for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE; adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
} else if (!qlcnic_port_eswitch_cfg_capability(adapter)) {
for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
qlcnic_enable_eswitch(adapter, i, 1);
} }
kfree(pci_info); kfree(pci_info);
...@@ -2069,7 +2071,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, ...@@ -2069,7 +2071,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
return err; return err;
} }
qlcnic_dcb_init_dcbnl_ops(adapter); qlcnic_dcb_init_dcbnl_ops(adapter->dcb);
return 0; return 0;
} }
...@@ -2164,17 +2166,6 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter) ...@@ -2164,17 +2166,6 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd); qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
} }
static int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
{
return __qlcnic_register_dcb(adapter);
}
void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter)
{
kfree(adapter->dcb);
adapter->dcb = NULL;
}
static int static int
qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
...@@ -2183,6 +2174,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -2183,6 +2174,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct qlcnic_hardware_context *ahw; struct qlcnic_hardware_context *ahw;
int err, pci_using_dac = -1; int err, pci_using_dac = -1;
char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */ char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
struct qlcnic_dcb *dcb;
if (pdev->is_virtfn) if (pdev->is_virtfn)
return -ENODEV; return -ENODEV;
...@@ -2303,8 +2295,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -2303,8 +2295,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->flags |= QLCNIC_NEED_FLR; adapter->flags |= QLCNIC_NEED_FLR;
if (adapter->dcb && qlcnic_dcb_attach(adapter)) dcb = adapter->dcb;
qlcnic_clear_dcb_ops(adapter);
if (dcb && qlcnic_dcb_attach(dcb))
qlcnic_clear_dcb_ops(dcb);
} else if (qlcnic_83xx_check(adapter)) { } else if (qlcnic_83xx_check(adapter)) {
adapter->max_drv_tx_rings = 1; adapter->max_drv_tx_rings = 1;
...@@ -2449,7 +2443,7 @@ static void qlcnic_remove(struct pci_dev *pdev) ...@@ -2449,7 +2443,7 @@ static void qlcnic_remove(struct pci_dev *pdev)
qlcnic_cancel_idc_work(adapter); qlcnic_cancel_idc_work(adapter);
ahw = adapter->ahw; ahw = adapter->ahw;
qlcnic_dcb_free(adapter); qlcnic_dcb_free(adapter->dcb);
unregister_netdev(netdev); unregister_netdev(netdev);
qlcnic_sriov_cleanup(adapter); qlcnic_sriov_cleanup(adapter);
...@@ -3327,7 +3321,7 @@ qlcnic_attach_work(struct work_struct *work) ...@@ -3327,7 +3321,7 @@ qlcnic_attach_work(struct work_struct *work)
return; return;
} }
attach: attach:
qlcnic_dcb_get_info(adapter); qlcnic_dcb_get_info(adapter->dcb);
if (netif_running(netdev)) { if (netif_running(netdev)) {
if (qlcnic_up(adapter, netdev)) if (qlcnic_up(adapter, netdev))
...@@ -3352,6 +3346,8 @@ qlcnic_attach_work(struct work_struct *work) ...@@ -3352,6 +3346,8 @@ qlcnic_attach_work(struct work_struct *work)
static int static int
qlcnic_check_health(struct qlcnic_adapter *adapter) qlcnic_check_health(struct qlcnic_adapter *adapter)
{ {
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_fw_dump *fw_dump = &ahw->fw_dump;
u32 state = 0, heartbeat; u32 state = 0, heartbeat;
u32 peg_status; u32 peg_status;
int err = 0; int err = 0;
...@@ -3376,7 +3372,7 @@ qlcnic_check_health(struct qlcnic_adapter *adapter) ...@@ -3376,7 +3372,7 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
if (adapter->need_fw_reset) if (adapter->need_fw_reset)
goto detach; goto detach;
if (adapter->ahw->reset_context && qlcnic_auto_fw_reset) if (ahw->reset_context && qlcnic_auto_fw_reset)
qlcnic_reset_hw_context(adapter); qlcnic_reset_hw_context(adapter);
return 0; return 0;
...@@ -3419,6 +3415,9 @@ qlcnic_check_health(struct qlcnic_adapter *adapter) ...@@ -3419,6 +3415,9 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
qlcnic_schedule_work(adapter, qlcnic_detach_work, 0); qlcnic_schedule_work(adapter, qlcnic_detach_work, 0);
QLCDB(adapter, DRV, "fw recovery scheduled.\n"); QLCDB(adapter, DRV, "fw recovery scheduled.\n");
} else if (!qlcnic_auto_fw_reset && fw_dump->enable &&
adapter->flags & QLCNIC_FW_RESET_OWNER) {
qlcnic_dump_fw(adapter);
} }
return 1; return 1;
......
...@@ -1187,41 +1187,38 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) ...@@ -1187,41 +1187,38 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
} }
if (ops_index == ops_cnt) { if (ops_index == ops_cnt) {
dev_info(&adapter->pdev->dev, dev_info(dev, "Skipping unknown entry opcode %d\n",
"Invalid entry type %d, exiting dump\n",
entry->hdr.type); entry->hdr.type);
goto error; entry->hdr.flags |= QLCNIC_DUMP_SKIP;
entry_offset += entry->hdr.offset;
continue;
} }
/* Collect dump for this entry */ /* Collect dump for this entry */
dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer); dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer);
if (!qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, dump)) if (!qlcnic_valid_dump_entry(dev, entry, dump)) {
entry->hdr.flags |= QLCNIC_DUMP_SKIP; entry->hdr.flags |= QLCNIC_DUMP_SKIP;
entry_offset += entry->hdr.offset;
continue;
}
buf_offset += entry->hdr.cap_size; buf_offset += entry->hdr.cap_size;
entry_offset += entry->hdr.offset; entry_offset += entry->hdr.offset;
buffer = fw_dump->data + buf_offset; buffer = fw_dump->data + buf_offset;
} }
if (dump_size != buf_offset) {
dev_info(&adapter->pdev->dev, fw_dump->clr = 1;
"Captured(%d) and expected size(%d) do not match\n", snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name);
buf_offset, dump_size); dev_info(dev, "%s: Dump data %d bytes captured, template header size %d bytes\n",
goto error; adapter->netdev->name, fw_dump->size, tmpl_hdr->size);
} else { /* Send a udev event to notify availability of FW dump */
fw_dump->clr = 1; kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg);
snprintf(mesg, sizeof(mesg), "FW_DUMP=%s",
adapter->netdev->name);
dev_info(&adapter->pdev->dev, "%s: Dump data, %d bytes captured\n",
adapter->netdev->name, fw_dump->size);
/* Send a udev event to notify availability of FW dump */
kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg);
return 0;
}
error:
if (fw_dump->use_pex_dma) if (fw_dump->use_pex_dma)
dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE, dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
fw_dump->dma_buffer, fw_dump->phys_addr); fw_dump->dma_buffer, fw_dump->phys_addr);
vfree(fw_dump->data);
return -EINVAL; return 0;
} }
void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter) void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter)
......
...@@ -500,6 +500,7 @@ static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) ...@@ -500,6 +500,7 @@ static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter)
static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
int pci_using_dac) int pci_using_dac)
{ {
struct qlcnic_dcb *dcb;
int err; int err;
INIT_LIST_HEAD(&adapter->vf_mc_list); INIT_LIST_HEAD(&adapter->vf_mc_list);
...@@ -533,8 +534,10 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, ...@@ -533,8 +534,10 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
if (err) if (err)
goto err_out_send_channel_term; goto err_out_send_channel_term;
if (adapter->dcb && qlcnic_dcb_attach(adapter)) dcb = adapter->dcb;
qlcnic_clear_dcb_ops(adapter);
if (dcb && qlcnic_dcb_attach(dcb))
qlcnic_clear_dcb_ops(dcb);
err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac);
if (err) if (err)
...@@ -1577,7 +1580,7 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter) ...@@ -1577,7 +1580,7 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
if (err) if (err)
goto err_out_term_channel; goto err_out_term_channel;
qlcnic_dcb_get_info(adapter); qlcnic_dcb_get_info(adapter->dcb);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment