diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 385a4d5c7c25..f419965f3a6d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1352,6 +1352,8 @@ enum op_codes { #define QLCNIC_ENABLE_FW_DUMP 0xaddfeed #define QLCNIC_DISABLE_FW_DUMP 0xbadfeed #define QLCNIC_FORCE_FW_RESET 0xdeaddead +#define QLCNIC_SET_QUIESCENT 0xadd00010 +#define QLCNIC_RESET_QUIESCENT 0xadd00020 struct qlcnic_dump_operations { enum op_codes opcode; @@ -1559,6 +1561,7 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring) } extern const struct ethtool_ops qlcnic_ethtool_ops; +extern const struct ethtool_ops qlcnic_ethtool_failed_ops; struct qlcnic_nic_template { int (*config_bridged_mode) (struct qlcnic_adapter *, u32); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 89ddf7f7d7df..f19e11ed7b09 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -1132,6 +1132,11 @@ qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + if (!fw_dump->tmpl_hdr) { + netdev_err(adapter->netdev, "FW Dump not supported\n"); + return -ENOTSUPP; + } + if (fw_dump->clr) dump->len = fw_dump->tmpl_hdr->size + fw_dump->size; else @@ -1150,6 +1155,11 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW Dump not supported\n"); + return -ENOTSUPP; + } + if (!fw_dump->clr) { netdev_info(netdev, "Dump not available\n"); return -EINVAL; @@ -1180,9 +1190,14 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) int ret = 0; struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + u32 state; switch (val->flag) { case QLCNIC_FORCE_FW_DUMP_KEY: + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } if (!fw_dump->enable) { netdev_info(netdev, "FW dump not enabled\n"); return ret; @@ -1196,35 +1211,47 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) qlcnic_dev_request_reset(adapter); break; case QLCNIC_DISABLE_FW_DUMP: - if (fw_dump->enable) { + if (fw_dump->enable && fw_dump->tmpl_hdr) { netdev_info(netdev, "Disabling FW dump\n"); fw_dump->enable = 0; } - break; + return ret; case QLCNIC_ENABLE_FW_DUMP: - if (!fw_dump->enable && fw_dump->tmpl_hdr) { + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } + if (!fw_dump->enable) { netdev_info(netdev, "Enabling FW dump\n"); fw_dump->enable = 1; } - break; + return ret; case QLCNIC_FORCE_FW_RESET: netdev_info(netdev, "Forcing a FW reset\n"); qlcnic_dev_request_reset(adapter); adapter->flags &= ~QLCNIC_FW_RESET_OWNER; - break; + return ret; + case QLCNIC_SET_QUIESCENT: + case QLCNIC_RESET_QUIESCENT: + state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + netdev_info(netdev, "Device in FAILED state\n"); + return ret; default: + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } if (val->flag > QLCNIC_DUMP_MASK_MAX || val->flag < QLCNIC_DUMP_MASK_MIN) { netdev_info(netdev, "Invalid dump level: 0x%x\n", val->flag); - ret = -EINVAL; - goto out; + return -EINVAL; } fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff; netdev_info(netdev, "Driver mask changed to: 0x%x\n", fw_dump->tmpl_hdr->drv_cap_mask); } -out: return ret; } @@ -1258,3 +1285,10 @@ const struct ethtool_ops qlcnic_ethtool_ops = { .get_dump_data = qlcnic_get_dump_data, .set_dump = qlcnic_set_dump, }; + +const struct ethtool_ops qlcnic_ethtool_failed_ops = { + .get_settings = qlcnic_get_settings, + .get_drvinfo = qlcnic_get_drvinfo, + .set_msglevel = qlcnic_set_msglevel, + .get_msglevel = qlcnic_get_msglevel, +}; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index a52819303d1b..e6a77feeb44a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -704,6 +704,8 @@ enum { #define QLCNIC_DEV_FAILED 0x6 #define QLCNIC_DEV_QUISCENT 0x7 +#define QLCNIC_DEV_BADBAD 0xbad0bad0 + #define QLCNIC_DEV_NPAR_NON_OPER 0 /* NON Operational */ #define QLCNIC_DEV_NPAR_OPER 1 /* NPAR Operational */ #define QLCNIC_DEV_NPAR_OPER_TIMEO 30 /* Operational time out */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 75c32e875fef..5c4713521d4c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -338,6 +338,10 @@ static const struct net_device_ops qlcnic_netdev_ops = { #endif }; +static const struct net_device_ops qlcnic_netdev_failed_ops = { + .ndo_open = qlcnic_open, +}; + static struct qlcnic_nic_template qlcnic_ops = { .config_bridged_mode = qlcnic_config_bridged_mode, .config_led = qlcnic_config_led, @@ -1623,8 +1627,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = adapter->nic_ops->start_firmware(adapter); if (err) { - dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n"); - goto err_out_decr_ref; + dev_err(&pdev->dev, "Loading fw failed. Please Reboot\n" + "\t\tIf reboot doesn't help, try flashing the card\n"); + goto err_out_maintenance_mode; } if (qlcnic_read_mac_addr(adapter)) @@ -1695,6 +1700,18 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; + +err_out_maintenance_mode: + netdev->netdev_ops = &qlcnic_netdev_failed_ops; + SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops); + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "failed to register net device\n"); + goto err_out_decr_ref; + } + pci_set_drvdata(pdev, adapter); + qlcnic_create_diag_entries(adapter); + return 0; } static void __devexit qlcnic_remove(struct pci_dev *pdev) @@ -1831,8 +1848,14 @@ qlcnic_resume(struct pci_dev *pdev) static int qlcnic_open(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); int err; + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) { + netdev_err(netdev, "Device in FAILED state\n"); + return -EIO; + } + netif_carrier_off(netdev); err = qlcnic_attach(adapter); @@ -3018,6 +3041,12 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter) return; state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) { + netdev_err(adapter->netdev, + "Device is in FAILED state, Please Reboot\n"); + qlcnic_api_unlock(adapter); + return; + } if (state == QLCNIC_DEV_READY) { QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_NEED_RESET); @@ -3061,6 +3090,9 @@ qlcnic_cancel_fw_work(struct qlcnic_adapter *adapter) while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) msleep(10); + if (!adapter->fw_work.work.func) + return; + cancel_delayed_work_sync(&adapter->fw_work); } @@ -4280,6 +4312,7 @@ static void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) { struct device *dev = &adapter->pdev->dev; + u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); if (device_create_bin_file(dev, &bin_attr_port_stats)) dev_info(dev, "failed to create port stats sysfs entry"); @@ -4288,14 +4321,19 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) return; if (device_create_file(dev, &dev_attr_diag_mode)) dev_info(dev, "failed to create diag_mode sysfs entry\n"); - if (device_create_file(dev, &dev_attr_beacon)) - dev_info(dev, "failed to create beacon sysfs entry"); if (device_create_bin_file(dev, &bin_attr_crb)) dev_info(dev, "failed to create crb sysfs entry\n"); if (device_create_bin_file(dev, &bin_attr_mem)) dev_info(dev, "failed to create mem sysfs entry\n"); + + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; + if (device_create_bin_file(dev, &bin_attr_pci_config)) dev_info(dev, "failed to create pci config sysfs entry"); + if (device_create_file(dev, &dev_attr_beacon)) + dev_info(dev, "failed to create beacon sysfs entry"); + if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) return; if (device_create_bin_file(dev, &bin_attr_esw_config)) @@ -4314,16 +4352,19 @@ static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) { struct device *dev = &adapter->pdev->dev; + u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); device_remove_bin_file(dev, &bin_attr_port_stats); if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) return; device_remove_file(dev, &dev_attr_diag_mode); - device_remove_file(dev, &dev_attr_beacon); device_remove_bin_file(dev, &bin_attr_crb); device_remove_bin_file(dev, &bin_attr_mem); + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; device_remove_bin_file(dev, &bin_attr_pci_config); + device_remove_file(dev, &dev_attr_beacon); if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) return; device_remove_bin_file(dev, &bin_attr_esw_config);