lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Thu, 23 Aug 2012 21:07:14 -0400 From: Sony Chacko <sony.chacko@...gic.com> To: davem@...emloft.net Cc: netdev@...r.kernel.org, Dept_NX_Linux_NIC_Driver@...gic.com, Sony Chacko <sony.chacko@...gic.com> Subject: [PATCH net-next 10/15] qlcnic: 83xx CNA inter driver communication mechanism From: Sony Chacko <sony.chacko@...gic.com> Inter driver communication mechanism for 83xx CNA function drivers Signed-off-by: Rajesh Borundia <rajesh.borundia@...gic.com> Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@...gic.com> Signed-off-by: Sony Chacko <sony.chacko@...gic.com> --- drivers/net/ethernet/qlogic/qlcnic/Makefile | 3 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 8 +- .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 102 +- .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 1991 ++++++++++++++++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 8 +- .../net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 5 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 144 ++- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 12 + 9 files changed, 2211 insertions(+), 64 deletions(-) create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile index f87d98f..799325d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/Makefile +++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile @@ -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_83xx_hw.o qlcnic_minidump.o + qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \ + qlcnic_83xx_init.o diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index f95477e..eaaa049 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1067,7 +1067,9 @@ struct qlcnic_adapter { struct msix_entry *msix_entries; + struct workqueue_struct *qlcnic_wq; struct delayed_work fw_work; + struct delayed_work idc_aen_work; struct qlcnic_filter_hash fhash; @@ -1160,9 +1162,9 @@ struct qlcnic_eswitch { #define QLCNIC_SWITCH_PORT_MIRRORING BIT_4 }; - /* Return codes for Error handling */ -#define QL_STATUS_INVALID_PARAM -1 +#define QL_STATUS_INVALID_PARAM -1 +#define QL_STATUS_UNSUPPORTED_CMD -2 #define MAX_BW 100 /* % of link speed */ #define MAX_VLAN_ID 4095 @@ -1628,7 +1630,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); void qlcnic_dev_request_reset(struct qlcnic_adapter *, u32); int qlcnic_validate_max_rss(u8, u8); -int qlcnic_set_max_rss(struct qlcnic_adapter *, u8); +int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, size_t); int qlcnic_setup_intr(struct qlcnic_adapter *, u8); int qlcnic_msix_tx_poll(struct napi_struct *, int); int qlcnic_poll(struct napi_struct *, int); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index c430723..fc50e1d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -161,11 +161,21 @@ struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { 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, .config_ipaddr = qlcnic_83xx_config_ipaddr, .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, }; +void +qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw) +{ + ahw->hw_ops = &qlcnic_83xx_hw_ops; + ahw->reg_tbl = (u32 *) qlcnic_83xx_reg_tbl; + ahw->ext_reg_tbl = (u32 *) qlcnic_83xx_ext_reg_tbl; +} + int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter) { @@ -200,8 +210,8 @@ qlcnic_83xx_get_fw_version(struct qlcnic_adapter *adapter) fw_build = QLCRD(adapter, QLCNIC_FW_VERSION_SUB); adapter->fw_version = QLCNIC_VERSION_CODE(fw_major, fw_minor, fw_build); - dev_info(&pdev->dev, "firmware version %d.%d.%d\n", - fw_major, fw_minor, fw_build); + dev_info(&pdev->dev, "Driver v%s, firmware version %d.%d.%d\n", + QLCNIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build); return adapter->fw_version; } @@ -333,14 +343,14 @@ qlcnic_83xx_disable_tx_intr(struct qlcnic_adapter *adapter, irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter) { u32 intr_val; + struct qlcnic_hardware_context *ahw = adapter->ahw; + int retries = 0; intr_val = readl(adapter->tgt_status_reg); - if (!QLCNIC_83XX_VALID_INTX_BIT30(intr_val)) { - dev_info(&adapter->pdev->dev, - "Invalid legacy interrupt:0x%x\n", intr_val); + if (!QLCNIC_83XX_VALID_INTX_BIT31(intr_val)) return IRQ_NONE; - } + if (QLCNIC_83XX_INTX_FUNC(intr_val) != adapter->ahw->pci_func) { dev_info(&adapter->pdev->dev, "Interrupt for wrong func: 0x%x\n", intr_val); @@ -348,8 +358,23 @@ irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter) } /* clear the interrupt trigger control register */ writel(0, adapter->isr_int_vec); - /* Delay for 4 msec to fix bug in chip, remove it for B0 chip rev */ - mdelay(4); + + /* Legacy Workaround for A0 & B0 */ + do { + intr_val = readl(adapter->tgt_status_reg); + if (QLCNIC_83XX_INTX_FUNC(intr_val) != ahw->pci_func) + break; + retries++; + } while (QLCNIC_83XX_VALID_INTX_BIT30(intr_val) && + (retries < QLC_83XX_LEGACY_INTX_MAX_RETRY)); + + if (retries == QLC_83XX_LEGACY_INTX_MAX_RETRY) { + dev_info(&adapter->pdev->dev, + "Reached maximum retries to clear legacy interrupt\n"); + return IRQ_NONE; + } + + mdelay(QLC_83XX_LEGACY_INTX_DELAY); return IRQ_HANDLED; } @@ -1532,6 +1557,10 @@ qlcnic_83xx_mbx_op(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd) dev_info(&adapter->pdev->dev, "Mailbox not available, 0x%x, collect FW dump\n", mbx_val); + /* Take FW dump */ + qlcnic_83xx_idc_request_reset(adapter, + QLCNIC_FORCE_FW_DUMP_KEY); + cmd->rsp.arg[0] = QLCNIC_RCODE_TIMEOUT; spin_unlock_irqrestore(&ahw->mbx_lock, flags); return cmd->rsp.arg[0]; @@ -1557,8 +1586,9 @@ poll: if (rsp != QLCNIC_RCODE_TIMEOUT) { if (opcode == QLCNIC_MBX_LINK_EVENT) { for (i = 0; i < rsp_num; i++) - fw[i] = le32_to_cpu(readl(QLCNIC_MBX_FW(ahw, - i))); + fw[i] = + le32_to_cpu(readl(QLCNIC_MBX_FW(ahw, i))); + qlcnic_83xx_handle_link_aen(adapter, fw); /* clear fw mbx control register */ QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); @@ -1567,14 +1597,29 @@ poll: goto poll; } else if (opcode == QLCNIC_MBX_COMP_EVENT) { for (i = 0; i < rsp_num; i++) - fw[i] = le32_to_cpu(readl(QLCNIC_MBX_FW(ahw, - i))); + fw[i] = + le32_to_cpu(readl(QLCNIC_MBX_FW(ahw, i))); qlcnic_83xx_handle_idc_comp_aen(adapter, fw); /* clear fw mbx control register */ QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); if (mbx_val) goto poll; + } else if (opcode == QLCNIC_MBX_REQUEST_EVENT) { + /* IDC Request Notification */ + for (i = 0; i < rsp_num; i++) + fw[i] = + le32_to_cpu(readl(QLCNIC_MBX_FW(ahw, i))); + for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++) + adapter->ahw->mbox_aen[i] = + QLCNIC_MBX_RSP(fw[i]); + queue_delayed_work(adapter->qlcnic_wq, + &adapter->idc_aen_work, 0); + /* clear fw mbx control register */ + QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); + mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); + if (mbx_val) + goto poll; } else if ((mbx_err_code == QLCNIC_MBX_RSP_OK) || (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) { for (i = 0; i < cmd->rsp.num; i++) @@ -1591,6 +1636,9 @@ poll: dev_info(&adapter->pdev->dev, "MBX command 0x%x timed out\n", opcode); qlcnic_dump_mbx(adapter, cmd); + /* Take FW dump */ + qlcnic_83xx_idc_request_reset(adapter, + QLCNIC_FORCE_FW_DUMP_KEY); } /* clear fw mbx control register */ QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); @@ -1631,24 +1679,23 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx, return 0; } -void qlcnic_83xx_handle_idc_request_aen(struct qlcnic_adapter *adapter) +void qlcnic_83xx_idc_aen_work(struct work_struct *work) { + struct qlcnic_adapter *adapter; struct qlcnic_cmd_args cmd; int i, err = 0; + adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work); + /* Issue idc ack mbx command */ - if (test_and_clear_bit(QLC_83XX_MBX_AEN_ACK, - &adapter->ahw->idc.status)) { - adapter->ahw->hw_ops->alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_IDC_ACK); - for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++) - cmd.req.arg[i] = adapter->ahw->mbox_aen[i]; - err = adapter->ahw->hw_ops->mbx_cmd(adapter, &cmd); - if (err) - dev_info(&adapter->pdev->dev, - "%s: Mailbox IDC ACK failed.\n", __func__); - qlcnic_free_mbx_args(&cmd); - } + adapter->ahw->hw_ops->alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK); + for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++) + cmd.req.arg[i] = adapter->ahw->mbox_aen[i]; + err = adapter->ahw->hw_ops->mbx_cmd(adapter, &cmd); + if (err) + dev_info(&adapter->pdev->dev, + "%s: Mailbox IDC ACK failed.\n", __func__); + qlcnic_free_mbx_args(&cmd); } static void @@ -1683,7 +1730,9 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) case QLCNIC_MBX_REQUEST_EVENT: for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++) adapter->ahw->mbox_aen[i] = QLCNIC_MBX_RSP(event[i]); - set_bit(QLC_83XX_MBX_AEN_ACK, &adapter->ahw->idc.status); + + queue_delayed_work(adapter->qlcnic_wq, + &adapter->idc_aen_work, 0); break; case QLCNIC_MBX_TIME_EXTEND_EVENT: break; @@ -1739,6 +1788,7 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter) memset(&sds_mbx, 0, sds_mbx_size); sds = &recv_ctx->sds_rings[i]; sds->consumer = 0; + memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds)); sds_mbx.phy_addr = cpu_to_le64(sds->phys_addr); sds_mbx.sds_ring_size = cpu_to_le16(sds->num_desc); if (adapter->flags & QLCNIC_MSIX_ENABLED) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c new file mode 100644 index 0000000..1bb9ae9 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -0,0 +1,1991 @@ +#include "qlcnic.h" +#include "qlcnic_hw.h" + +static int +qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter); +static int +qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); + +static const char * const qlcnic_83xx_idc_states[] = { + "Unknown", + "Cold", + "Init", + "Ready", + "Need Reset", + "Need Quiesce", + "Failed", + "Quiesce" +}; + +/* IDC Device States */ +enum qlc_83xx_states { + QLC_83XX_IDC_DEV_UNKNOWN, + QLC_83XX_IDC_DEV_COLD, + QLC_83XX_IDC_DEV_INIT, + QLC_83XX_IDC_DEV_READY, + QLC_83XX_IDC_DEV_NEED_RESET, + QLC_83XX_IDC_DEV_NEED_QUISCENT, + QLC_83XX_IDC_DEV_FAILED, + QLC_83XX_IDC_DEV_QUISCENT +}; + +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 = 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 + * + * Return 0 if all functions have acknowledged the reset request. + * Check ACK wait limit and clear the functions which failed to ACK + * + **/ +static int +qlcnic_83xx_idc_check_reset_ack_reg(struct qlcnic_adapter *adapter) +{ + u32 ack, presence, val; + + 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) { + if (qlcnic_83xx_idc_check_timeout(adapter, + QLC_83XX_IDC_RESET_TIMEOUT_SECS)) { + /* 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: adapter structure + * + * Handle context deletion and recreation request from transmit routine + * + * Returns -EBUSY or Success (0) + * + **/ +static int +qlcnic_83xx_idc_tx_soft_reset(struct qlcnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EBUSY; + + netif_device_detach(netdev); + qlcnic_down(adapter, netdev); + qlcnic_up(adapter, netdev); + netif_device_attach(netdev); + clear_bit(__QLCNIC_RESETTING, &adapter->state); + dev_err(&adapter->pdev->dev, "%s:\n", __func__); + + adapter->netdev->trans_start = jiffies; + + return 0; +} + +/** + * qlcnic_83xx_idc_detach_driver + * + * @adapter: adapter structure + * Detach net interface, stop TX and cleanup resources before the HW reset. + * Returns: None + * + **/ +static void +qlcnic_83xx_idc_detach_driver(struct qlcnic_adapter *adapter) +{ + int i; + + struct net_device *netdev = adapter->netdev; + + netif_device_detach(netdev); + /* Disable mailbox interrupt */ + QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0); + qlcnic_down(adapter, netdev); + for (i = 0; i < adapter->ahw->num_msix; i++) { + adapter->ahw->intr_tbl[i].id = i; + adapter->ahw->intr_tbl[i].enabled = 0; + adapter->ahw->intr_tbl[i].src = 0; + } +} + +/** + * qlcnic_83xx_idc_attach_driver + * + * @adapter: adapter structure + * + * Re-attach and re-enable net interface + * Returns: None + * + **/ +static void +qlcnic_83xx_idc_attach_driver(struct qlcnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + + if (netif_running(netdev)) { + if (qlcnic_up(adapter, netdev)) + goto done; + + qlcnic_restore_indev_addr(netdev, NETDEV_UP); + } +done: + netif_device_attach(netdev); + if (netif_running(netdev)) { + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } +} + +static int +qlcnic_83xx_idc_enter_failed_state(struct qlcnic_adapter *adapter, + int lock) +{ + if (lock) { + if (qlcnic_83xx_lock_driver(adapter)) + return -EBUSY; + } + + qlcnic_83xx_idc_clear_registers(adapter, 0); + QLCWRX(adapter->ahw, QLC_83XX_IDC_DEV_STATE, + QLC_83XX_IDC_DEV_FAILED); + if (lock) + qlcnic_83xx_unlock_driver(adapter); + + qlcnic_83xx_idc_log_state_history(adapter); + dev_info(&adapter->pdev->dev, "Device will enter failed state\n"); + + return 0; +} + +static int +qlcnic_83xx_idc_enter_init_state(struct qlcnic_adapter *adapter, int lock) +{ + if (lock) { + if (qlcnic_83xx_lock_driver(adapter)) + return -EBUSY; + } + + QLCWRX(adapter->ahw, QLC_83XX_IDC_DEV_STATE, QLC_83XX_IDC_DEV_INIT); + + if (lock) + qlcnic_83xx_unlock_driver(adapter); + + return 0; +} + +static int +qlcnic_83xx_idc_enter_need_quiscent(struct qlcnic_adapter *adapter, int lock) +{ + if (lock) { + if (qlcnic_83xx_lock_driver(adapter)) + return -EBUSY; + } + + QLCWRX(adapter->ahw, QLC_83XX_IDC_DEV_STATE, + QLC_83XX_IDC_DEV_NEED_QUISCENT); + + if (lock) + qlcnic_83xx_unlock_driver(adapter); + + return 0; +} + +static int +qlcnic_83xx_idc_enter_need_reset_state(struct qlcnic_adapter *adapter, int lock) +{ + if (lock) { + if (qlcnic_83xx_lock_driver(adapter)) + return -EBUSY; + } + + QLCWRX(adapter->ahw, QLC_83XX_IDC_DEV_STATE, + QLC_83XX_IDC_DEV_NEED_RESET); + + if (lock) + qlcnic_83xx_unlock_driver(adapter); + + return 0; +} + +static int +qlcnic_83xx_idc_enter_ready_state(struct qlcnic_adapter *adapter, int lock) +{ + if (lock) { + if (qlcnic_83xx_lock_driver(adapter)) + return -EBUSY; + } + + QLCWRX(adapter->ahw, QLC_83XX_IDC_DEV_STATE, + QLC_83XX_IDC_DEV_READY); + if (lock) + qlcnic_83xx_unlock_driver(adapter); + + return 0; +} + +/** + * qlcnic_83xx_idc_find_reset_owner_id + * + * @adapter: adapter structure + * + * NIC gets precedence over ISCSI and ISCSI has precedence over FCOE. + * Within the same class, function with lowest PCI ID assumes ownership + * + * Returns: reset owner id or failure indication (-EIO) + * + **/ +static int +qlcnic_83xx_idc_find_reset_owner_id(struct qlcnic_adapter *adapter) +{ + u32 reg, reg1, reg2, i, j, owner, class; + + reg1 = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_PARTITION_INFO_1); + reg2 = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_PARTITION_INFO_2); + owner = QLCNIC_TYPE_NIC; + i = 0; + j = 0; + reg = reg1; + + do { + class = (((reg & (0xF << j*4)) >> j*4) & 0x3); + if (class == owner) + break; + if (i == (QLC_83XX_IDC_MAX_FUNC_PER_PARTITION_INFO - 1)) { + reg = reg2; + j = 0; + } else { + j++; + } + + if (i == (QLC_83XX_IDC_MAX_CNA_FUNCTIONS - 1)) { + if (owner == QLCNIC_TYPE_NIC) + owner = QLCNIC_TYPE_ISCSI; + else if (owner == QLCNIC_TYPE_ISCSI) + owner = QLCNIC_TYPE_FCOE; + else if (owner == QLCNIC_TYPE_FCOE) + return -EIO; + + reg = reg1; + j = 0; + i = 0; + } + + } while (i++ < QLC_83XX_IDC_MAX_CNA_FUNCTIONS); + + return i; +} + +static int +qlcnic_83xx_idc_restart_hw(struct qlcnic_adapter *adapter, int lock) +{ + int ret = 0; + + ret = qlcnic_83xx_restart_hw(adapter); + + if (ret) { + qlcnic_83xx_idc_enter_failed_state(adapter, lock); + } else { + qlcnic_83xx_idc_clear_registers(adapter, lock); + ret = qlcnic_83xx_idc_enter_ready_state(adapter, lock); + } + + return ret; +} + +static int +qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter) +{ + u32 status; + + status = QLCRD(adapter, QLCNIC_PEG_HALT_STATUS1); + + if (status & QLCNIC_RCODE_FATAL_ERROR) { + dev_err(&adapter->pdev->dev, + "peg halt status1=0x%x\n", status); + if (QLCNIC_FWERROR_CODE(status) == + QLCNIC_FWERROR_FAN_FAILURE) { + dev_err(&adapter->pdev->dev, + "On board active cooling fan failed. " + "Device has been halted.\n"); + dev_err(&adapter->pdev->dev, + "Replace the adapter.\n"); + return -EIO; + } + } + + return 0; +} + +static int +qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) +{ + qlcnic_83xx_enable_mbx_intrpt(adapter); + if ((adapter->flags & QLCNIC_MSIX_ENABLED)) { + if (qlcnic_83xx_config_intrpt(adapter, 1)) { + netdev_err(adapter->netdev, + "Failed to enable mbx intr\n"); + return -EIO; + } + } + + if (qlcnic_83xx_configure_opmode(adapter)) { + qlcnic_83xx_idc_enter_failed_state(adapter, 1); + return -EIO; + } + + if (adapter->nic_ops->init_driver(adapter)) { + qlcnic_83xx_idc_enter_failed_state(adapter, 1); + return -EIO; + } + + qlcnic_83xx_idc_attach_driver(adapter); + + return 0; +} + +static void +qlcnic_83xx_idc_update_idc_params(struct qlcnic_adapter *adapter) +{ + qlcnic_83xx_idc_update_drv_presence_reg(adapter, 1, 1); + clear_bit(__QLCNIC_RESETTING, &adapter->state); + set_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); + qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); + set_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status); + adapter->ahw->idc.quiesce_req = 0; + adapter->ahw->idc.delay = QLC_83XX_IDC_FW_POLL_DELAY; + adapter->ahw->idc.err_code = 0; + adapter->ahw->idc.collect_dump = 0; +} + +/** + * qlcnic_83xx_idc_ready_state_entry_action + * + * @adapter: adapter structure + * + * Perform ready state initialization, this routine will get invoked only + * once from READY state. + * + * Returns: -EIO or 0 + * + **/ +int +qlcnic_83xx_idc_ready_state_entry_action(struct qlcnic_adapter *adapter) +{ + if (adapter->ahw->idc.prev_state != QLC_83XX_IDC_DEV_READY) { + qlcnic_83xx_idc_update_idc_params(adapter); + /* Re-attach the device if required */ + if ((adapter->ahw->idc.prev_state == + QLC_83XX_IDC_DEV_NEED_RESET) || + (adapter->ahw->idc.prev_state == + QLC_83XX_IDC_DEV_INIT)) { + if (qlcnic_83xx_idc_reattach_driver(adapter)) + return -EIO; + + } + } + + return 0; +} + +/** + * qlcnic_83xx_idc_vnic_pf_ready_state_entry_action + * + * @adapter: adapter structure + * + * Ensure vNIC mode privileged function becomes ready only when vNIC mode is + * set ready by vNIC management function. + * If vNIC mode is ready, perform ready state initialization. + * + * Returns: -EIO or 0 + * + **/ +int +qlcnic_83xx_idc_vnic_pf_ready_state_entry_action(struct qlcnic_adapter *adapter) +{ + u32 state; + struct qlcnic_hardware_context *ahw = adapter->ahw; + + /* Privileged function waits till mgmt function enables VNIC mode */ + state = QLCRDX(adapter->ahw, QLC_83XX_VNIC_STATE); + if (state != QLCNIC_DEV_NPAR_OPER) { + if (!ahw->idc.vnic_wait_limit--) { + qlcnic_83xx_idc_enter_failed_state(adapter, 1); + return -EIO; + } + + dev_info(&adapter->pdev->dev, "vNIC mode disabled\n"); + return -EIO; + + } else { + /* Perform one time initialization from ready state */ + if (adapter->ahw->idc.vnic_state != QLCNIC_DEV_NPAR_OPER) { + qlcnic_83xx_idc_update_idc_params(adapter); + + /* If the previous state is UNKNOWN, device will be + already attached properly */ + if (adapter->ahw->idc.prev_state != + QLC_83XX_IDC_DEV_UNKNOWN) { + if (qlcnic_83xx_idc_reattach_driver(adapter)) + return -EIO; + } + + adapter->ahw->idc.vnic_state = QLCNIC_DEV_NPAR_OPER; + dev_info(&adapter->pdev->dev, "vNIC mode enabled\n"); + } + } + + return 0; +} + +static int +qlcnic_83xx_idc_unknown_state_handler(struct qlcnic_adapter *adapter) +{ + adapter->ahw->idc.err_code = -EIO; + dev_err(&adapter->pdev->dev, + "%s: Device in unknown state\n", __func__); + return 0; +} + +/** + * qlcnic_83xx_idc_cold_state_handler + * + * @adapter: adapter structure + * + * If HW is up and running device will enter READY state. + * If host file system based firmware image needs to be loaded, device is + * forced to start with the file firmware image. + * + * Returns: Success (0) or -EIO + * + **/ +static int +qlcnic_83xx_idc_cold_state_handler(struct qlcnic_adapter *adapter) +{ + qlcnic_83xx_idc_update_drv_presence_reg(adapter, 1, 0); + qlcnic_83xx_idc_update_audit_reg(adapter, 1, 0); + if (adapter->ahw->fw_info.load_from_file) { + qlcnic_83xx_idc_restart_hw(adapter, 0); + } else { + if (qlcnic_83xx_check_hw_status(adapter)) { + qlcnic_83xx_idc_enter_failed_state(adapter, 0); + return -EIO; + } else { + qlcnic_83xx_idc_enter_ready_state(adapter, 0); + } + } + return 0; +} + +/** + * qlcnic_83xx_idc_init_state_handler + * + * @adapter: adapter structure + * + * Reset owner will restart the device from this state. + * Device will enter failed state if it remains + * in this state for more than DEV_INIT time limit. + * + * Returns: + * + **/ +static int +qlcnic_83xx_idc_init_state_handler(struct qlcnic_adapter *adapter) +{ + int ret = 0; + u32 owner; + + if (adapter->ahw->idc.prev_state == QLC_83XX_IDC_DEV_NEED_RESET) { + owner = qlcnic_83xx_idc_find_reset_owner_id(adapter); + if (adapter->ahw->pci_func == owner) + ret = qlcnic_83xx_idc_restart_hw(adapter, 1); + } else { + ret = qlcnic_83xx_idc_check_timeout(adapter, + QLC_83XX_IDC_INIT_TIMEOUT_SECS); + return ret; + } + + return ret; +} + +/** + * qlcnic_83xx_idc_ready_state_handler + * + * @adapter: adapter structure + * + * Perform IDC protocol specicifed actions after monitoring device temperature, + * FW status, reset and quiesce requests. + * + * Returns: 0 or -EIO + * + **/ +static int +qlcnic_83xx_idc_ready_state_handler(struct qlcnic_adapter *adapter) +{ + u32 val; + struct qlcnic_hardware_context *ahw = adapter->ahw; + int ret = 0; + + /* Perform NIC configuration based ready state entry actions */ + if (ahw->idc.ready_state_entry_action(adapter)) + return -EIO; + + /* Check temperature */ + if (qlcnic_check_temp(adapter)) { + if (adapter->ahw->temp == QLCNIC_TEMP_PANIC) { + qlcnic_83xx_idc_check_fan_failure(adapter); + dev_err(&adapter->pdev->dev, + "Error: device temperature %d above limits\n", + adapter->ahw->temp); + clear_bit(QLC_83XX_MBX_READY, + &adapter->ahw->idc.status); + set_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_83xx_idc_detach_driver(adapter); + qlcnic_83xx_idc_enter_failed_state(adapter, 1); + return -EIO; + } + } + + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + + /* Check FW hearbeat */ + ret = qlcnic_83xx_check_heartbeat(adapter); + if (ret) { + adapter->flags |= QLCNIC_FW_HANG; + if (!(val & QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY)) { + clear_bit(QLC_83XX_MBX_READY, + &adapter->ahw->idc.status); + set_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_83xx_idc_enter_need_reset_state(adapter, 1); + } + return -EIO; + } + + if ((val & QLC_83XX_IDC_GRACEFULL_RESET) || + adapter->ahw->idc.collect_dump) { + /* Move to need reset state and prepare for reset */ + qlcnic_83xx_idc_enter_need_reset_state(adapter, 1); + return ret; + + } + + /* Check for soft reset request */ + if (adapter->ahw->reset_context && + !(val & QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY)) { + qlcnic_83xx_idc_tx_soft_reset(adapter); + return ret; + } + + /* Move to need quiesce state if requested */ + if (adapter->ahw->idc.quiesce_req) { + qlcnic_83xx_idc_enter_need_quiscent(adapter, 1); + qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); + return ret; + } + + return ret; +} + +/** + * qlcnic_83xx_idc_need_reset_state_handler + * + * @adapter: adapter structure + * + * Device will remain in this state until: + * Reset request ACK's are recieved from all the functions + * Wait time exceeds max time limit + * + * Source States: + * Destination States: + * + * Returns: 0 or -EIO + * + **/ +static int +qlcnic_83xx_idc_need_reset_state_handler(struct qlcnic_adapter *adapter) +{ + int ret = 0; + + if (adapter->ahw->idc.prev_state != QLC_83XX_IDC_DEV_NEED_RESET) { + qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); + qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); + set_bit(__QLCNIC_RESETTING, &adapter->state); + clear_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); + qlcnic_83xx_idc_detach_driver(adapter); + } + + /* Check ACK from other functions */ + ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter); + if (ret) { + dev_info(&adapter->pdev->dev, + "%s: Waiting for reset ACK\n", __func__); + return 0; + } + + /* Transit to INIT state and restart the HW */ + qlcnic_83xx_idc_enter_init_state(adapter, 1); + + return ret; +} + +static int +qlcnic_83xx_idc_need_quiesce_state_handler(struct qlcnic_adapter *adapter) +{ + dev_err(&adapter->pdev->dev, "%s: TBD\n", __func__); + return 0; +} + +static int +qlcnic_83xx_idc_failed_state_handler(struct qlcnic_adapter *adapter) +{ + dev_err(&adapter->pdev->dev, + "%s: please reboot!!\n", __func__); + adapter->ahw->idc.err_code = -EIO; + + return 0; +} + +static int +qlcnic_83xx_idc_quiesce_state_handler(struct qlcnic_adapter *adapter) +{ + dev_info(&adapter->pdev->dev, + "%s: TBD\n", __func__); + return 0; +} + +/** + * qlcnic_83xx_idc_check_dev_state_validity + * + * @adapter: adapter structure + * @state: IDC destination state + * + * Ensure state transitions are according to the IDC protocol. + * + * Returns: Success (0) or FAILURE (1) + * + **/ +static int +qlcnic_83xx_idc_check_dev_state_validity(struct qlcnic_adapter *adapter, + u32 state) +{ + u32 cur, prev, next; + + cur = adapter->ahw->idc.curr_state; + prev = adapter->ahw->idc.prev_state; + next = state; + + if ((next < QLC_83XX_IDC_DEV_COLD) || + (next > QLC_83XX_IDC_DEV_QUISCENT)) { + dev_err(&adapter->pdev->dev, + "%s: curr %d, prev %d, next state %d is invalid\n", + __func__, cur, prev, state); + return 1; + } + + if ((cur == QLC_83XX_IDC_DEV_UNKNOWN) && + (prev == QLC_83XX_IDC_DEV_UNKNOWN)) { + if ((next != QLC_83XX_IDC_DEV_COLD) && + (next != QLC_83XX_IDC_DEV_READY)) { + dev_err(&adapter->pdev->dev, + "%s: invalid transition, cur %d prev %d next %d\n", + __func__, cur, prev, next); + return 1; + } + } + + if (next == QLC_83XX_IDC_DEV_INIT) { + if ((prev != QLC_83XX_IDC_DEV_INIT) && + (prev != QLC_83XX_IDC_DEV_COLD) && + (prev != QLC_83XX_IDC_DEV_NEED_RESET)) { + dev_err(&adapter->pdev->dev, + "%s: invalid transition, cur %d prev %d next %d\n", + __func__, cur, prev, next); + return 1; + } + } + + return 0; +} + +/** + * qlcnic_83xx_idc_poll_dev_state + * + * @work: kernel work queue structure used to schedule the function + * + * Poll device state periodically and perform state specific + * actions defined by Inter Driver Communication (IDC) protocol. + * + * Returns: None + * + **/ +void qlcnic_83xx_idc_poll_dev_state(struct work_struct *work) +{ + struct qlcnic_adapter *adapter; + u32 state; + + adapter = container_of(work, struct qlcnic_adapter, fw_work.work); + state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE); + + if (qlcnic_83xx_idc_check_dev_state_validity(adapter, state)) { + qlcnic_83xx_idc_log_state_history(adapter); + adapter->ahw->idc.curr_state = QLC_83XX_IDC_DEV_UNKNOWN; + } else { + adapter->ahw->idc.curr_state = state; + } + + switch (adapter->ahw->idc.curr_state) { + case QLC_83XX_IDC_DEV_READY: + qlcnic_83xx_idc_ready_state_handler(adapter); + break; + + case QLC_83XX_IDC_DEV_NEED_RESET: + qlcnic_83xx_idc_need_reset_state_handler(adapter); + break; + + case QLC_83XX_IDC_DEV_NEED_QUISCENT: + qlcnic_83xx_idc_need_quiesce_state_handler(adapter); + break; + + case QLC_83XX_IDC_DEV_FAILED: + qlcnic_83xx_idc_failed_state_handler(adapter); + return; + + case QLC_83XX_IDC_DEV_INIT: + qlcnic_83xx_idc_init_state_handler(adapter); + break; + + case QLC_83XX_IDC_DEV_QUISCENT: + qlcnic_83xx_idc_quiesce_state_handler(adapter); + break; + + default: + qlcnic_83xx_idc_unknown_state_handler(adapter); + return; + } + + adapter->ahw->idc.prev_state = adapter->ahw->idc.curr_state; + + /* Re-schedule the function */ + if (test_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status)) + qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state, + adapter->ahw->idc.delay); +} + +static void +qlcnic_83xx_setup_idc_parameters(struct qlcnic_adapter *adapter) +{ + u32 idc_params, val; + + if (qlcnic_83xx_lockless_flash_read_u32(adapter, + QLC_83XX_IDC_FLASH_PARAM_ADDR, + (u8 *)&idc_params, 1)) { + dev_info(&adapter->pdev->dev, + "%s: failed to get IDC params from flash.\n", __func__); + adapter->dev_init_timeo = QLC_83XX_IDC_INIT_TIMEOUT_SECS; + adapter->reset_ack_timeo = QLC_83XX_IDC_RESET_TIMEOUT_SECS; + } else { + adapter->dev_init_timeo = idc_params & 0xFFFF; + adapter->reset_ack_timeo = ((idc_params >> 16) & 0xFFFF); + } + + adapter->ahw->idc.curr_state = QLC_83XX_IDC_DEV_UNKNOWN; + adapter->ahw->idc.prev_state = QLC_83XX_IDC_DEV_UNKNOWN; + adapter->ahw->idc.delay = QLC_83XX_IDC_FW_POLL_DELAY; + adapter->ahw->idc.err_code = 0; + adapter->ahw->idc.collect_dump = 0; + adapter->ahw->idc.name = (char **)qlcnic_83xx_idc_states; + + clear_bit(__QLCNIC_RESETTING, &adapter->state); + set_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); + set_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status); + + /* Check if reset recovery is disabled */ + if (!auto_fw_reset) { + /* Propagate do not reset request to other functions */ + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + val = val | QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY; + QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); + } +} + +/** + * qlcnic_83xx_idc_first_to_load_function_handler + * + * @adapter: adapter structure + * + * Peform first to load function specific initialization + * + * Returns: Success(0) or Failure(-EIO) + * + **/ +static int +qlcnic_83xx_idc_first_to_load_function_handler(struct qlcnic_adapter *adapter) +{ + u32 state, val; + + if (qlcnic_83xx_lock_driver(adapter)) + return -EIO; + + /* Clear driver lock register */ + QLCWRX(adapter->ahw, QLC_83XX_RECOVER_DRV_LOCK, 0); + + if (qlcnic_83xx_idc_update_major_version(adapter, 0)) { + qlcnic_83xx_unlock_driver(adapter); + return -EIO; + } + + state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE); + if (qlcnic_83xx_idc_check_dev_state_validity(adapter, state)) { + qlcnic_83xx_unlock_driver(adapter); + return -EIO; + } + + adapter->ahw->idc.curr_state = state; + + /* First to load function should cold boot the device */ + if (state == QLC_83XX_IDC_DEV_COLD) + qlcnic_83xx_idc_cold_state_handler(adapter); + + /* Check if reset recovery is enabled */ + if (auto_fw_reset) { + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + val = val & ~QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY; + QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); + } + + qlcnic_83xx_unlock_driver(adapter); + + return 0; +} + +/** + * qlcnic_83xx_idc_init + * + * @adapter: adapter structure + * + * Initialize IDC task parameters + * + * Returns: Success(0) or Failure(-EIO) + * + **/ +int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter) +{ + int ret = -EIO; + + qlcnic_83xx_setup_idc_parameters(adapter); + + if (!qlcnic_83xx_idc_check_driver_presence_reg(adapter)) { + if (qlcnic_83xx_idc_first_to_load_function_handler(adapter)) + return -EIO; + } else { + if (qlcnic_83xx_idc_check_major_version(adapter)) + return -EIO; + } + + if (qlcnic_83xx_get_reset_instruction_template(adapter)) + return ret; + + qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); + + return 0; +} + +void +qlcnic_83xx_idc_exit(struct qlcnic_adapter *adapter) +{ + int id; + u32 val; + + while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + usleep_range(10000, 11000); + + id = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID); + id = id & 0xFF; + + if (id == adapter->portnum) { + dev_err(&adapter->pdev->dev, + "%s: wait for lock recovery.. %d\n", __func__, id); + msleep(20); + id = QLCRDX(adapter->ahw, QLC_83XX_DRV_LOCK_ID); + id = id & 0xFF; + } + + /* Clear driver presence bit */ + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE); + val = val & ~(1 << adapter->portnum); + QLCWRX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE, val); + + clear_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status); + clear_bit(__QLCNIC_RESETTING, &adapter->state); + + cancel_delayed_work_sync(&adapter->fw_work); +} + +void +qlcnic_83xx_idc_request_reset(struct qlcnic_adapter *adapter, u32 key) +{ + u32 val; + + if (qlcnic_83xx_lock_driver(adapter)) { + dev_err(&adapter->pdev->dev, + "%s:failed, please retry\n", __func__); + return; + } + + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + if ((val & QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY) || !auto_fw_reset) { + dev_err(&adapter->pdev->dev, + "%s:failed, device in non reset mode\n", __func__); + qlcnic_83xx_unlock_driver(adapter); + return; + } + + if (key == QLCNIC_FORCE_FW_RESET) { + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + val = val | QLC_83XX_IDC_GRACEFULL_RESET; + QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); + } else if (key == QLCNIC_FORCE_FW_DUMP_KEY) { + adapter->ahw->idc.collect_dump = 1; + } + + qlcnic_83xx_unlock_driver(adapter); + return; +} + +static int +qlcnic_83xx_copy_bootloader(struct qlcnic_adapter *adapter) +{ + u8 *p_cache; + u32 src, count, size; + u64 dest; + int ret = -EIO; + + src = QLC_83XX_BOOTLOADER_FLASH_ADDR; + dest = QLCRDX(adapter->ahw, QLCNIC_BOOTLOADER_ADDR); + size = QLCRDX(adapter->ahw, QLCNIC_BOOTLOADER_SIZE); + + /* alignmnet check */ + if (size & 0xF) + size = (size + 16) & ~0xF; + + count = size/16; + + p_cache = kzalloc(size, GFP_KERNEL); + if (p_cache == NULL) { + dev_err(&adapter->pdev->dev, + "Failed to allocate memory for boot loader cache\n"); + return -ENOMEM; + } + + ret = qlcnic_83xx_lockless_flash_read_u32(adapter, src, + p_cache, size/sizeof(u32)); + if (ret) { + kfree(p_cache); + return ret; + } + + /* 16 byte write to MS memory */ + ret = qlcnic_83xx_ms_mem_write_128b(adapter, dest, + (u32 *)p_cache, size/16); + if (ret) { + kfree(p_cache); + return ret; + } + + kfree(p_cache); + + return ret; +} + +static int +qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter) +{ + u32 dest, *p_cache; + u64 addr ; + u8 data[16] ; + size_t size ; + int i, ret = -EIO; + + dest = QLCRDX(adapter->ahw, QLCNIC_FW_IMAGE_ADDR); + + size = (adapter->ahw->fw_info.fw->size & ~0xF); + p_cache = (u32 *)adapter->ahw->fw_info.fw->data; + addr = (u64)dest; + + ret = qlcnic_83xx_ms_mem_write_128b(adapter, addr, + (u32 *)p_cache, size/16); + if (ret) { + dev_err(&adapter->pdev->dev, "MS memory write failed\n"); + release_firmware(adapter->ahw->fw_info.fw); + adapter->ahw->fw_info.fw = NULL; + return -EIO; + } + + /* Check 128 bit alignment */ + if (adapter->ahw->fw_info.fw->size & 0xF) { + addr = dest + size; + for (i = 0; i < (adapter->ahw->fw_info.fw->size & 0xF); i++) + data[i] = adapter->ahw->fw_info.fw->data[size + i]; + + for (; i < 16 ; i++) + data[i] = 0; + + ret = qlcnic_83xx_ms_mem_write_128b(adapter, addr, + (u32 *)data, 1); + if (ret) { + dev_err(&adapter->pdev->dev, + "MS memory write failed\n"); + release_firmware(adapter->ahw->fw_info.fw); + adapter->ahw->fw_info.fw = NULL; + return -EIO; + } + } + + release_firmware(adapter->ahw->fw_info.fw); + adapter->ahw->fw_info.fw = NULL; + + return 0 ; +} + +int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev) +{ + u32 heartbeat, ret = -EIO; + int retries = QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT; + + p_dev->heartbeat = QLCRD(p_dev, QLCNIC_PEG_ALIVE_COUNTER); + + do { + msleep(QLCNIC_HEARTBEAT_PERIOD_MSECS); + heartbeat = QLCRD(p_dev, QLCNIC_PEG_ALIVE_COUNTER); + if (heartbeat != p_dev->heartbeat) { + ret = QLCNIC_RCODE_SUCCESS; + break; + } + } while (--retries); + + if (ret) + dev_err(&p_dev->pdev->dev, "firmware hang detected\n"); + + return ret; +} + +static int +qlcnic_83xx_check_cmd_peg_status(struct qlcnic_adapter *p_dev) +{ + int retries = QLCNIC_CMDPEG_CHECK_RETRY_COUNT; + u32 val; + + do { + val = QLCRD(p_dev, QLCNIC_CMDPEG_STATE); + if (val == QLC_83xx_CMDPEG_COMPLETE) + return 0; + + msleep(QLCNIC_CMDPEG_CHECK_DELAY); + } while (--retries); + + dev_err(&p_dev->pdev->dev, "%s: failed, state = 0x%x\n", __func__, val); + return -EIO; +} + +int +qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev) +{ + int err; + + err = qlcnic_83xx_check_cmd_peg_status(p_dev); + if (err) + return err; + + err = qlcnic_83xx_check_heartbeat(p_dev); + if (err) + return err; + + return err; +} + +static int +qlcnic_83xx_poll_reg(struct qlcnic_adapter *p_dev, u32 addr, int duration, + u32 test_mask, u32 test_result) +{ + u32 value; + int err, timeout_error; + u8 retries; + + value = qlcnic_83xx_rd_reg_indirect(p_dev, addr, &err); + + /* poll every 1/10 of the total duration */ + retries = duration/10; + + do { + if ((value & test_mask) != test_result) { + timeout_error = 1; + msleep(duration/10); + value = qlcnic_83xx_rd_reg_indirect(p_dev, addr, &err); + } else { + timeout_error = 0; + break; + } + } while (retries--); + + if (timeout_error) { + p_dev->ahw->reset.seq_error++ ; + dev_err(&p_dev->pdev->dev, + "%s: 0x%08x 0x%08x 0x%08x\n", + __func__, value, test_mask, test_result); + } + + return timeout_error; +} + +static int +qlcnic_83xx_validate_reset_template_checksum(struct qlcnic_adapter *p_dev) +{ + u32 sum = 0; + u16 *buff = (u16 *)p_dev->ahw->reset.buff; + int count = p_dev->ahw->reset.hdr->size / sizeof(u16); + + while (count-- > 0) + sum += *buff++; + + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + if (~sum) { + return 0; + } else { + dev_err(&p_dev->pdev->dev, "%s: failed\n", __func__); + return -1; + } +} + +int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *p_dev) +{ + u8 *p_buff; + u32 addr, u32_count; + + p_dev->ahw->reset.seq_error = 0; + p_dev->ahw->reset.buff = + kzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE, GFP_KERNEL); + + if (p_dev->ahw->reset.buff == NULL) { + dev_err(&p_dev->pdev->dev, + "%s: resource allocation failed\n", __func__); + return -ENOMEM; + } + + p_buff = p_dev->ahw->reset.buff; + addr = QLC_83XX_RESET_TEMPLATE_ADDR; + + u32_count = sizeof(struct qlcnic_83xx_reset_template_hdr) / sizeof(u32); + + /* Copy template header from flash */ + if (qlcnic_83xx_flash_read_u32(p_dev, addr, p_buff, u32_count)) { + dev_err(&p_dev->pdev->dev, "%s: flash read failed\n", __func__); + return -EIO; + } + + p_dev->ahw->reset.hdr = + (struct qlcnic_83xx_reset_template_hdr *) p_dev->ahw->reset.buff; + + addr = QLC_83XX_RESET_TEMPLATE_ADDR + p_dev->ahw->reset.hdr->hdr_size; + p_buff = p_dev->ahw->reset.buff + p_dev->ahw->reset.hdr->hdr_size; + u32_count = (p_dev->ahw->reset.hdr->size - + p_dev->ahw->reset.hdr->hdr_size)/sizeof(u32); + + /* Copy rest of the template */ + if (qlcnic_83xx_flash_read_u32(p_dev, addr, p_buff, u32_count)) { + dev_err(&p_dev->pdev->dev, "%s: flash read failed\n", __func__); + return -EIO; + } + + /* Integrity check */ + if (qlcnic_83xx_validate_reset_template_checksum(p_dev)) + return -EIO; + + /* Get STOP, START, INIT sequence offsets */ + p_dev->ahw->reset.init_offset = p_dev->ahw->reset.buff + + p_dev->ahw->reset.hdr->init_seq_offset; + + p_dev->ahw->reset.start_offset = p_dev->ahw->reset.buff + + p_dev->ahw->reset.hdr->start_seq_offset; + + p_dev->ahw->reset.stop_offset = p_dev->ahw->reset.buff + + p_dev->ahw->reset.hdr->hdr_size; + return 0; +} + +static void +qlcnic_83xx_read_write_crb_reg(struct qlcnic_adapter *p_dev, u32 raddr, + u32 waddr) +{ + int err, value; + + value = qlcnic_83xx_rd_reg_indirect(p_dev, raddr, &err); + qlcnic_83xx_wrt_reg_indirect(p_dev, waddr, value); +} + +/* Read Modify Write CRB register. */ +static void +qlcnic_83xx_rmw_crb_reg(struct qlcnic_adapter *p_dev, + u32 raddr, u32 waddr, struct qlcnic_83xx_rmw *p_rmw_hdr) +{ + int value, err; + if (p_rmw_hdr->index_a) + value = p_dev->ahw->reset.array[p_rmw_hdr->index_a]; + else + value = qlcnic_83xx_rd_reg_indirect(p_dev, raddr, &err); + + value &= p_rmw_hdr->test_mask; + value <<= p_rmw_hdr->shl; + value >>= p_rmw_hdr->shr; + value |= p_rmw_hdr->or_value; + value ^= p_rmw_hdr->xor_value; + qlcnic_83xx_wrt_reg_indirect(p_dev, waddr, value); + return; +} + +static void +qlcnic_83xx_write_list(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + int i; + struct qlcnic_83xx_entry *p_entry; + + p_entry = (struct qlcnic_83xx_entry *)((char *)p_hdr + + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_wrt_reg_indirect(p_dev, p_entry->arg1, + p_entry->arg2); + if (p_hdr->delay) + udelay((u32)(p_hdr->delay)); + } +} + +static void +qlcnic_83xx_read_write_list(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + int i; + struct qlcnic_83xx_entry *p_entry; + + p_entry = (struct qlcnic_83xx_entry *)((char *)p_hdr + + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_read_write_crb_reg(p_dev, p_entry->arg1, + p_entry->arg2); + if (p_hdr->delay) + udelay((u32)(p_hdr->delay)); + } +} + +static void +qlcnic_83xx_poll_list(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + long delay; + struct qlcnic_83xx_entry *p_entry; + struct qlcnic_83xx_poll *p_poll; + int i, err; + + p_poll = (struct qlcnic_83xx_poll *) + ((char *)p_hdr + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + + p_entry = (struct qlcnic_83xx_entry *)((char *)p_poll + + sizeof(struct qlcnic_83xx_poll)); + + delay = (long)p_hdr->delay; + + if (!delay) { + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_poll_reg(p_dev, p_entry->arg1, + delay, p_poll->test_mask, p_poll->test_value); + } + } else { + for (i = 0; i < p_hdr->count; i++, p_entry++) { + if (delay) { + if (qlcnic_83xx_poll_reg(p_dev, + p_entry->arg1, delay, + p_poll->test_mask, + p_poll->test_value)){ + qlcnic_83xx_rd_reg_indirect(p_dev, + p_entry->arg1, &err); + qlcnic_83xx_rd_reg_indirect(p_dev, + p_entry->arg2, &err); + + dev_err(&p_dev->pdev->dev, + "Timeout Error: poll list "); + dev_err(&p_dev->pdev->dev, + "item_num = %d entry_num = %d\n", + i, p_dev->ahw->reset.seq_index); + } + } + } + } +} + +static void +qlcnic_83xx_poll_write_list(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + long delay; + struct qlcnic_83xx_quad_entry *p_entry; + struct qlcnic_83xx_poll *p_poll; + int i; + + p_poll = (struct qlcnic_83xx_poll *)((char *)p_hdr + + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + p_entry = (struct qlcnic_83xx_quad_entry *)((char *)p_poll + + sizeof(struct qlcnic_83xx_poll)); + + delay = (long)p_hdr->delay; + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_wrt_reg_indirect(p_dev, + p_entry->dr_addr, p_entry->dr_value); + qlcnic_83xx_wrt_reg_indirect(p_dev, + p_entry->ar_addr, p_entry->ar_value); + if (delay) { + if (qlcnic_83xx_poll_reg(p_dev, + p_entry->ar_addr, delay, + p_poll->test_mask, + p_poll->test_value)){ + dev_err(&p_dev->pdev->dev, + "Timeout Error: poll list "); + dev_err(&p_dev->pdev->dev, + "item_num = %d entry_num = %d\n", + i, p_dev->ahw->reset.seq_index); + } + } + } +} + +static void +qlcnic_83xx_read_modify_write(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + struct qlcnic_83xx_entry *p_entry; + struct qlcnic_83xx_rmw *p_rmw_hdr; + int i; + + p_rmw_hdr = (struct qlcnic_83xx_rmw *)((char *)p_hdr + + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + + p_entry = (struct qlcnic_83xx_entry *)((char *)p_rmw_hdr + + sizeof(struct qlcnic_83xx_rmw)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_rmw_crb_reg(p_dev, p_entry->arg1, + p_entry->arg2, p_rmw_hdr); + if (p_hdr->delay) + udelay((u32)(p_hdr->delay)); + } +} + +static void +qlcnic_83xx_pause(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + if (p_hdr->delay) + mdelay((u32)((long)p_hdr->delay)); +} + +static void +qlcnic_83xx_poll_read_list(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + long delay; + int index, i, err; + struct qlcnic_83xx_quad_entry *p_entry; + struct qlcnic_83xx_poll *p_poll; + + p_poll = (struct qlcnic_83xx_poll *) + ((char *)p_hdr + sizeof(struct qlcnic_83xx_reset_entry_hdr)); + + p_entry = (struct qlcnic_83xx_quad_entry *) + ((char *)p_poll + sizeof(struct qlcnic_83xx_poll)); + + delay = (long)p_hdr->delay; + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qlcnic_83xx_wrt_reg_indirect(p_dev, p_entry->ar_addr, + p_entry->ar_value); + if (delay) { + if (qlcnic_83xx_poll_reg(p_dev, p_entry->ar_addr, delay, + p_poll->test_mask, p_poll->test_value)){ + dev_err(&p_dev->pdev->dev, + "Timeout Error: poll list "); + dev_err(&p_dev->pdev->dev, + "item_num = %d entry_num = %d\n", i, + p_dev->ahw->reset.seq_index); + } else { + index = p_dev->ahw->reset.array_index; + p_dev->ahw->reset.array[index++] = + qlcnic_83xx_rd_reg_indirect(p_dev, + p_entry->dr_addr, &err); + if (index == QLC_83XX_MAX_RESET_SEQ_ENTRIES) + p_dev->ahw->reset.array_index = 1; + } + } + } +} + +static inline void +qlcnic_83xx_seq_end(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + p_dev->ahw->reset.seq_end = 1; +} + +static void +qlcnic_83xx_template_end(struct qlcnic_adapter *p_dev, + struct qlcnic_83xx_reset_entry_hdr *p_hdr) +{ + p_dev->ahw->reset.template_end = 1; + + if (p_dev->ahw->reset.seq_error == 0) + dev_err(&p_dev->pdev->dev, + "HW restart process completed successfully.\n"); + else + dev_err(&p_dev->pdev->dev, + "HW restart completed with timeout errors.\n"); +} + +static void +qlcnic_83xx_execute_template_instructions(struct qlcnic_adapter *p_dev, + char *p_buff) +{ + int index, entries; + struct qlcnic_83xx_reset_entry_hdr *p_hdr; + char *p_entry = p_buff; + + p_dev->ahw->reset.seq_end = 0; + p_dev->ahw->reset.template_end = 0; + entries = p_dev->ahw->reset.hdr->entries; + index = p_dev->ahw->reset.seq_index; + + for ( ; (!p_dev->ahw->reset.seq_end) && (index < entries); index++) { + + p_hdr = (struct qlcnic_83xx_reset_entry_hdr *)p_entry; + + switch (p_hdr->cmd) { + case OPCODE_NOP: + break; + case OPCODE_WRITE_LIST: + qlcnic_83xx_write_list(p_dev, p_hdr); + break; + case OPCODE_READ_WRITE_LIST: + qlcnic_83xx_read_write_list(p_dev, p_hdr); + break; + case OPCODE_POLL_LIST: + qlcnic_83xx_poll_list(p_dev, p_hdr); + break; + case OPCODE_POLL_WRITE_LIST: + qlcnic_83xx_poll_write_list(p_dev, p_hdr); + break; + case OPCODE_READ_MODIFY_WRITE: + qlcnic_83xx_read_modify_write(p_dev, p_hdr); + break; + case OPCODE_SEQ_PAUSE: + qlcnic_83xx_pause(p_dev, p_hdr); + break; + case OPCODE_SEQ_END: + qlcnic_83xx_seq_end(p_dev, p_hdr); + break; + case OPCODE_TMPL_END: + qlcnic_83xx_template_end(p_dev, p_hdr); + break; + case OPCODE_POLL_READ_LIST: + qlcnic_83xx_poll_read_list(p_dev, p_hdr); + break; + default: + dev_err(&p_dev->pdev->dev, + "%s: Unknown opcode 0x%04x in template entry %d\n", + __func__, p_hdr->cmd, index); + break; + } + + /* Set pointer to next entry in the sequence. */ + p_entry += p_hdr->size; + } + + p_dev->ahw->reset.seq_index = index; +} + +static void +qlcnic_83xx_stop_hw(struct qlcnic_adapter *p_dev) +{ + p_dev->ahw->reset.seq_index = 0; + qlcnic_83xx_execute_template_instructions(p_dev, + p_dev->ahw->reset.stop_offset); + + if (p_dev->ahw->reset.seq_end != 1) + dev_err(&p_dev->pdev->dev, "%s: failed\n", __func__); +} + +static void +qlcnic_83xx_start_hw(struct qlcnic_adapter *p_dev) +{ + qlcnic_83xx_execute_template_instructions(p_dev, + p_dev->ahw->reset.start_offset); + + if (p_dev->ahw->reset.template_end != 1) + dev_err(&p_dev->pdev->dev, "%s: failed\n", __func__); +} + +static void +qlcnic_83xx_init_hw(struct qlcnic_adapter *p_dev) +{ + qlcnic_83xx_execute_template_instructions(p_dev, + p_dev->ahw->reset.init_offset); + + if (p_dev->ahw->reset.seq_end != 1) + dev_err(&p_dev->pdev->dev, "%s: failed\n", __func__); +} + +int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter) +{ + u32 val; + int err = -EIO; + + qlcnic_83xx_stop_hw(adapter); + + /* Collect FW register dump if required */ + val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); + if (!(val & QLC_83XX_IDC_GRACEFULL_RESET)) + qlcnic_dump_fw(adapter); + + qlcnic_83xx_init_hw(adapter); + if (qlcnic_83xx_copy_bootloader(adapter)) + return err; + /* Boot either flash image or firmware image from host file system */ + if (adapter->ahw->fw_info.load_from_file) { + if (request_firmware(&adapter->ahw->fw_info.fw, + QLC_83XX_FW_FILE_NAME, &(adapter->pdev->dev))) { + dev_err(&adapter->pdev->dev, + "No file FW image, loading flash FW image.\n"); + QLCWR(adapter, QLCNIC_FW_IMG_VALID, + QLC_83XX_BOOT_FROM_FLASH); + } else { + if (qlcnic_83xx_copy_fw_file(adapter)) + return err; + QLCWR(adapter, QLCNIC_FW_IMG_VALID, + QLC_83XX_BOOT_FROM_FILE); + } + } else { + QLCWR(adapter, QLCNIC_FW_IMG_VALID, QLC_83XX_BOOT_FROM_FLASH); + } + qlcnic_83xx_start_hw(adapter); + if (qlcnic_83xx_check_hw_status(adapter)) + return -EIO; + + return 0; +} + +/** +* qlcnic_83xx_config_default_opmode +* +* @adapter: adapter structure +* +* Configure default driver operating mode +* +* Returns: 0 or -EIO +* */ +int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *adapter) +{ + u32 op_mode; + struct qlcnic_hardware_context *ahw = adapter->ahw; + + ahw->hw_ops->get_func_no(adapter); + op_mode = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); + + if (op_mode == QLC_83XX_DEFAULT_OPMODE) { + adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver; + adapter->ahw->idc.ready_state_entry_action = + qlcnic_83xx_idc_ready_state_entry_action; + } else { + return -EIO; + } + + return 0; +} + +int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter) +{ + struct qlcnic_info nic_info; + int err; + struct qlcnic_hardware_context *ahw = adapter->ahw; + + err = ahw->hw_ops->get_nic_info(adapter, &nic_info, ahw->pci_func); + if (err) + return -EIO; + + ahw->physical_port = (u8) nic_info.phys_port; + ahw->switch_mode = nic_info.switch_mode; + ahw->max_tx_ques = nic_info.max_tx_ques; + ahw->max_rx_ques = nic_info.max_rx_ques; + ahw->capabilities = nic_info.capabilities; + ahw->max_mac_filters = nic_info.max_mac_filters; + ahw->max_mtu = nic_info.max_mtu; + + if (ahw->capabilities & BIT_23) + ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE; + else + ahw->nic_mode = QLC_83XX_DEFAULT_MODE; + + return ahw->nic_mode; +} + +static int +qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter) +{ + int ret; + + ret = qlcnic_83xx_get_nic_configuration(adapter); + if (ret == -EIO) + return -EIO; + + if (ret == QLC_83XX_DEFAULT_MODE) { + if (qlcnic_83xx_config_default_opmode(adapter)) + return -EIO; + } + + return 0; +} + +static void +qlcnic_83xx_config_buff_descriptors(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + + if (ahw->port_type == QLCNIC_XGBE) { + adapter->num_rxd = DEFAULT_RCV_DESCRIPTORS_10G; + adapter->max_rxd = MAX_RCV_DESCRIPTORS_10G; + adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G; + adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G; + + } else if (ahw->port_type == QLCNIC_GBE) { + adapter->num_rxd = DEFAULT_RCV_DESCRIPTORS_1G; + adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_1G; + adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_1G; + adapter->max_rxd = MAX_RCV_DESCRIPTORS_1G; + } + adapter->num_txd = MAX_CMD_DESCRIPTORS; + adapter->max_rds_rings = MAX_RDS_RINGS; +} + +static int +qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter) +{ + int err = -EIO; + + qlcnic_83xx_get_minidump_template(adapter); + if (qlcnic_83xx_get_port_info(adapter)) + return err; + + qlcnic_83xx_config_buff_descriptors(adapter); + adapter->ahw->msix_supported = !!use_msi_x; + adapter->flags |= QLCNIC_ADAPTER_INITIALIZED; + + dev_info(&adapter->pdev->dev, "HAL Version: %d\n", + adapter->ahw->fw_hal_version); + + return 0; +} + +/* + * qlcnic_83xx_clear_function_resources + * + * @adapter: adapter structure + * + * Clean up the function resources if the function was used before + */ +static void +qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter) +{ + struct qlcnic_cmd_args cmd; + u32 presence_mask, audit_mask; + int status; + + presence_mask = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_PRESENCE); + audit_mask = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT); + + if (IS_QLCNIC_83XX_USED(adapter, presence_mask, audit_mask)) { + qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC); + cmd.req.arg[1] = cpu_to_le32(0 | BIT_31); + + status = adapter->ahw->hw_ops->mbx_cmd(adapter, &cmd); + + if (status) { + dev_err(&adapter->pdev->dev, + "Failed to clean up the function resources\n"); + } + + qlcnic_free_mbx_args(&cmd); + } +} + +/** + * qlcnic_83xx_init + * + * @adapter: adapter structure + * + * 83xx adapter specific initialization + * + * Returns: Success(0) or Failure(-EIO) + * + **/ +int +qlcnic_83xx_init(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + + if (qlcnic_83xx_check_hw_status(adapter)) + return -EIO; + + /* Initilaize 83xx mailbox spinlock */ + spin_lock_init(&ahw->mbx_lock); + + set_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); + qlcnic_83xx_clear_function_resources(adapter); + + if (!qlcnic_83xx_read_flash_descriptor_table(adapter)) + qlcnic_83xx_read_flash_mfg_id(adapter); + + if (qlcnic_83xx_idc_init(adapter)) + return -EIO; + + /* Configure default, SR-IOV or Virtual NIC mode of operation */ + if (qlcnic_83xx_configure_opmode(adapter)) + return -EIO; + + /* Perform operating mode specific initialization */ + if (adapter->nic_ops->init_driver(adapter)) + return -EIO; + + INIT_DELAYED_WORK(&adapter->idc_aen_work, qlcnic_83xx_idc_aen_work); + + /* register for NIC IDC AEN Events */ + qlcnic_83xx_register_nic_idc_func(adapter, 1); + + /* Periodically monitor device status */ + qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work); + + return adapter->ahw->idc.err_code; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index b1f3a4a..4b885c4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -680,7 +680,9 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter, cmd.req.arg[2] = LSD(nic_dma_t); cmd.req.arg[3] = (func_id << 16 | nic_size); err = adapter->ahw->hw_ops->mbx_cmd(adapter, &cmd); - if (err != QLCNIC_RCODE_SUCCESS) { + if (err == QLCNIC_RCODE_SUCCESS) + qlcnic_set_npar_data(adapter, nic_info, npar_info); + else { dev_err(&adapter->pdev->dev, "Failed to get nic info%d\n", err); err = -EIO; @@ -1043,8 +1045,8 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw, err_ret: dev_err(&adapter->pdev->dev, - "Invalid args func_esw %d port %d rx_ctx %d\n", - func_esw, port, rx_tx); + "Invalid arguments func_esw=%d port=%d rx_ctx=%d\n", + func_esw, port, rx_tx); return -EIO; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index bf6b1db..fa9e25e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -871,7 +871,7 @@ static int qlcnic_set_channels(struct net_device *dev, if (err) return err; - err = qlcnic_set_max_rss(adapter, channel->rx_count); + err = qlcnic_set_max_rss(adapter, channel->rx_count, 0); netdev_info(dev, "allocated 0x%x sds rings\n", adapter->max_sds_rings); return err; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 2d08234..958ad02 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -322,12 +322,13 @@ qlcnic_pcie_sem_lock(struct qlcnic_adapter *adapter, int sem, u32 id_reg) u32 qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr) { + int err; u32 data; if (QLCNIC_IS_82XX(adapter)) qlcnic_read_dump_reg(addr, adapter->ahw->pci_base0, &data); else - return -EIO; + data = qlcnic_83xx_rd_reg_indirect(adapter, addr, &err); return data; } @@ -335,6 +336,8 @@ void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data) { if (QLCNIC_IS_82XX(adapter)) qlcnic_write_dump_reg(addr, adapter->ahw->pci_base0, data); + else + qlcnic_83xx_wrt_reg_indirect(adapter, addr, data); } void diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 9304e71..9d1962f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -35,7 +35,6 @@ char qlcnic_driver_name[] = "qlcnic"; static const char qlcnic_driver_string[] = "QLogic 1/10 GbE " "Converged/Intelligent Ethernet Driver v" QLCNIC_LINUX_VERSIONID; -static struct workqueue_struct *qlcnic_wq; static int qlcnic_mac_learn; module_param(qlcnic_mac_learn, int, 0444); MODULE_PARM_DESC(qlcnic_mac_learn, "Mac Filter (0=disabled, 1=enabled)"); @@ -253,7 +252,7 @@ qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter, struct net_device *netdev) return 0; } -static int +int qlcnic_napi_add(struct qlcnic_adapter *adapter, struct net_device *netdev) { int ring; @@ -518,8 +517,9 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) /* subtract 2 as 2 vectors are required for mail box and tx ring */ adapter->max_sds_rings = num_msix - 2; - } else + } else { adapter->max_sds_rings = num_msix; + } dev_info(&pdev->dev, "using msi-x interrupts\n"); return err; } else if (err > 0) { @@ -579,9 +579,10 @@ qlcnic_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) if (!num_intr) num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS; - if (adapter->ahw->msix_supported) + if (adapter->ahw->msix_supported) { num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), num_intr)); + } else num_msix = 1; @@ -670,8 +671,11 @@ qlcnic_init_pci_info(struct qlcnic_adapter *adapter) j++; } - 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; + if (QLCNIC_IS_83XX(adapter)) + qlcnic_enable_eswitch(adapter, i, 1); + } kfree(pci_info); return 0; @@ -723,7 +727,7 @@ err_lock: return ret; } -static void +void qlcnic_check_vf(struct qlcnic_adapter *adapter, const struct pci_device_id *ent) { void __iomem *priv_op; @@ -1104,6 +1108,7 @@ qlcnic_reset_npar_config(struct qlcnic_adapter *adapter) struct qlcnic_npar_info *npar; struct qlcnic_info nic_info; u8 pci_func; + struct qlcnic_hardware_context *ahw = adapter->ahw; if (QLCNIC_IS_82XX(adapter)) if (!adapter->need_fw_reset) @@ -1113,13 +1118,12 @@ qlcnic_reset_npar_config(struct qlcnic_adapter *adapter) for (i = 0; i < adapter->ahw->act_pci_func; i++) { npar = &adapter->npars[i]; pci_func = npar->pci_func; - err = adapter->ahw->hw_ops->get_nic_info(adapter, - &nic_info, pci_func); + err = ahw->hw_ops->get_nic_info(adapter, &nic_info, pci_func); if (err) return err; nic_info.min_tx_bw = npar->min_bw; nic_info.max_tx_bw = npar->max_bw; - err = adapter->ahw->hw_ops->set_nic_info(adapter, &nic_info); + err = ahw->hw_ops->set_nic_info(adapter, &nic_info); if (err) return err; @@ -1176,6 +1180,8 @@ qlcnic_set_mgmt_operations(struct qlcnic_adapter *adapter) qlcnic_dev_set_npar_ready(adapter); + if (QLCNIC_IS_83XX(adapter)) + qlcnic_83xx_register_nic_idc_func(adapter, 1); return err; } @@ -1261,6 +1267,8 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { if (QLCNIC_IS_82XX(adapter)) handler = qlcnic_tmp_intr; + else + handler = qlcnic_83xx_tmp_intr; if (!QLCNIC_IS_MSI_FAMILY(adapter)) flags |= IRQF_SHARED; @@ -1560,6 +1568,7 @@ static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter) adapter->ahw->fw_dump.tmpl_hdr = NULL; } + kfree(adapter->ahw->reset.buff); adapter->ahw->fw_dump.tmpl_hdr = NULL; } @@ -1580,6 +1589,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) adapter->max_sds_rings = 1; adapter->ahw->diag_test = test; + adapter->ahw->linkup = 0; ret = qlcnic_attach(adapter); if (ret) { @@ -1605,6 +1615,8 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) if (QLCNIC_IS_82XX(adapter)) { QLCNIC_ENABLE_INTR(adapter, sds_ring->crb_intr_mask); + } else { + qlcnic_83xx_enable_intr(adapter, sds_ring); } } } @@ -1620,7 +1632,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) } /* Reset context in hardware only */ -static int +int qlcnic_reset_hw_context(struct qlcnic_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -1788,6 +1800,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE824X) { ahw->hw_ops = &qlcnic_hw_ops; ahw->reg_tbl = (u32 *) qlcnic_reg_tbl; + } else if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE834X) { + qlcnic_83xx_register_map(ahw); + } else { + goto err_out_free_hw_res; } err = qlcnic_setup_pci_map(pdev, ahw); @@ -1807,6 +1823,12 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->pdev = pdev; adapter->ahw = ahw; + adapter->qlcnic_wq = create_singlethread_workqueue("qlcnic"); + if (adapter->qlcnic_wq == NULL) { + dev_err(&pdev->dev, "Failed to create workqueue\n"); + goto err_out_free_netdev; + } + if (qlcnic_alloc_adapter_resources(adapter)) goto err_out_free_netdev; @@ -1836,6 +1858,19 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_hw; adapter->flags |= QLCNIC_NEED_FLR; + } else if (QLCNIC_IS_83XX(adapter)) { + qlcnic_83xx_check_vf(adapter, ent); + adapter->portnum = adapter->ahw->pci_func; + err = qlcnic_83xx_init(adapter); + if (err) { + dev_err(&pdev->dev, + "%s: failed. Please Reboot\n", __func__); + goto err_out_free_hw; + } + } else { + dev_err(&pdev->dev, + "%s: failed. Please Reboot\n", __func__); + goto err_out_free_hw; } if (qlcnic_read_mac_addr(adapter)) @@ -1852,6 +1887,11 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_out_disable_msi; + if (QLCNIC_IS_83XX(adapter)) { + err = qlcnic_83xx_setup_mbx_intr(adapter); + if (err) + goto err_out_disable_msi; + } err = qlcnic_setup_netdev(adapter, netdev, pci_using_dac); if (err) @@ -1883,6 +1923,11 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; err_out_disable_mbx_intr: + if (QLCNIC_IS_83XX(adapter)) { + if (adapter->flags & QLCNIC_MSIX_ENABLED) + qlcnic_83xx_config_intrpt(adapter, 0); + qlcnic_83xx_free_mbx_intr(adapter); + } err_out_disable_msi: qlcnic_teardown_intr(adapter); @@ -1927,6 +1972,12 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev) unregister_netdev(netdev); + if (QLCNIC_IS_83XX(adapter)) { + if (adapter->flags & QLCNIC_MSIX_ENABLED) + qlcnic_83xx_config_intrpt(adapter, 0); + qlcnic_83xx_free_mbx_intr(adapter); + } + qlcnic_detach(adapter); if (adapter->npars != NULL) kfree(adapter->npars); @@ -1953,6 +2004,10 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev) pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); + if (adapter->qlcnic_wq) { + destroy_workqueue(adapter->qlcnic_wq); + adapter->qlcnic_wq = NULL; + } qlcnic_free_adapter_resources(adapter); kfree(ahw); free_netdev(netdev); @@ -2076,6 +2131,11 @@ static int qlcnic_close(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); __qlcnic_down(adapter, netdev); + if (QLCNIC_IS_83XX(adapter)) { + qlcnic_83xx_register_nic_idc_func(adapter, 0); + cancel_delayed_work_sync(&adapter->idc_aen_work); + } + return 0; } @@ -2680,7 +2740,7 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter, u32 key) qlcnic_gb_set_gb3_mask(gb_val); QLCWR32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, gb_val); dev_info(&adapter->pdev->dev, - "Pause control frames disabled on all ports\n"); + "Pause control frames disabled on all ports\n"); adapter->need_fw_reset = 1; if (adapter->ahw->hw_ops->api_lock(adapter)) @@ -2721,7 +2781,7 @@ qlcnic_schedule_work(struct qlcnic_adapter *adapter, return; INIT_DELAYED_WORK(&adapter->fw_work, func); - queue_delayed_work(qlcnic_wq, &adapter->fw_work, + queue_delayed_work(adapter->qlcnic_wq, &adapter->fw_work, round_jiffies_relative(delay)); } @@ -2934,6 +2994,17 @@ static int qlcnic_attach_func(struct pci_dev *pdev) adapter->msix_entries = NULL; err = ahw->hw_ops->setup_intr(adapter, 0); + if (QLCNIC_IS_83XX(adapter)) { + err = qlcnic_83xx_setup_mbx_intr(adapter); + if (err) { + dev_err(&adapter->pdev->dev, + "failed to setup mbx interrupt\n"); + qlcnic_clr_all_drv_state(adapter, 1); + clear_bit(__QLCNIC_AER, &adapter->state); + goto done; + } + } + if (netif_running(netdev)) { err = qlcnic_attach(adapter); if (err) { @@ -2974,6 +3045,12 @@ static pci_ers_result_t qlcnic_io_error_detected(struct pci_dev *pdev, if (netif_running(netdev)) qlcnic_down(adapter, netdev); + if (QLCNIC_IS_83XX(adapter)) { + if (adapter->flags & QLCNIC_MSIX_ENABLED) + qlcnic_83xx_config_intrpt(adapter, 0); + qlcnic_83xx_free_mbx_intr(adapter); + } + qlcnic_detach(adapter); qlcnic_teardown_intr(adapter); @@ -3056,23 +3133,39 @@ int qlcnic_validate_max_rss(u8 max_hw, u8 val) return 0; } -int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data) +int +qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len) { + int err; struct net_device *netdev = adapter->netdev; - int err = 0; - - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) - return -EBUSY; + rtnl_lock(); netif_device_detach(netdev); if (netif_running(netdev)) __qlcnic_down(adapter, netdev); + + if (QLCNIC_IS_83XX(adapter)) { + if (adapter->flags & QLCNIC_MSIX_ENABLED) + qlcnic_83xx_config_intrpt(adapter, 0); + qlcnic_83xx_free_mbx_intr(adapter); + } + qlcnic_detach(adapter); qlcnic_teardown_intr(adapter); err = adapter->ahw->hw_ops->setup_intr(adapter, data); if (err) - netdev_info(netdev, "failed setting max_rss; rss disabled\n"); + dev_err(&adapter->pdev->dev, + "failed setting max_rss; rss disabled\n"); + + if (QLCNIC_IS_83XX(adapter)) { + err = qlcnic_83xx_setup_mbx_intr(adapter); + if (err) { + dev_err(&adapter->pdev->dev, + "failed to setup mbx interrupt\n"); + goto done; + } + } if (netif_running(netdev)) { err = qlcnic_attach(adapter); @@ -3083,17 +3176,19 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data) goto done; qlcnic_restore_indev_addr(netdev, NETDEV_UP); } + err = len; done: netif_device_attach(netdev); - clear_bit(__QLCNIC_RESETTING, &adapter->state); + rtnl_unlock(); return err; + } #ifdef CONFIG_INET #define is_qlcnic_netdev(dev) (dev->netdev_ops == &qlcnic_netdev_ops) -static void +void qlcnic_config_indev_addr(struct qlcnic_adapter *adapter, struct net_device *dev, unsigned long event) { @@ -3255,12 +3350,6 @@ static int __init qlcnic_init_module(void) printk(KERN_INFO "%s\n", qlcnic_driver_string); - qlcnic_wq = create_singlethread_workqueue("qlcnic"); - if (qlcnic_wq == NULL) { - printk(KERN_ERR "qlcnic: cannot create workqueue\n"); - return -ENOMEM; - } - #ifdef CONFIG_INET register_netdevice_notifier(&qlcnic_netdev_cb); register_inetaddr_notifier(&qlcnic_inetaddr_cb); @@ -3272,7 +3361,6 @@ static int __init qlcnic_init_module(void) unregister_inetaddr_notifier(&qlcnic_inetaddr_cb); unregister_netdevice_notifier(&qlcnic_netdev_cb); #endif - destroy_workqueue(qlcnic_wq); } return ret; @@ -3282,14 +3370,12 @@ module_init(qlcnic_init_module); static void __exit qlcnic_exit_module(void) { - pci_unregister_driver(&qlcnic_driver); #ifdef CONFIG_INET unregister_inetaddr_notifier(&qlcnic_inetaddr_cb); unregister_netdevice_notifier(&qlcnic_netdev_cb); #endif - destroy_workqueue(qlcnic_wq); } module_exit(qlcnic_exit_module); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index c77a016..9f743bf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -645,6 +645,9 @@ qlcnic_sysfs_get_port_stats(struct file *file, struct kobject *kobj, struct qlcnic_esw_statistics port_stats; int ret; + if (QLCNIC_IS_83XX(adapter)) + return QL_STATUS_UNSUPPORTED_CMD; + if (size != sizeof(struct qlcnic_esw_statistics)) return QL_STATUS_INVALID_PARAM; @@ -675,6 +678,9 @@ qlcnic_sysfs_get_esw_stats(struct file *file, struct kobject *kobj, struct qlcnic_esw_statistics esw_stats; int ret; + if (QLCNIC_IS_83XX(adapter)) + return QL_STATUS_UNSUPPORTED_CMD; + if (size != sizeof(struct qlcnic_esw_statistics)) return QL_STATUS_INVALID_PARAM; @@ -704,6 +710,9 @@ qlcnic_sysfs_clear_esw_stats(struct file *file, struct kobject *kobj, struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; + if (QLCNIC_IS_83XX(adapter)) + return QL_STATUS_UNSUPPORTED_CMD; + if (offset >= QLCNIC_NIU_MAX_XG_PORTS) return QL_STATUS_INVALID_PARAM; @@ -729,6 +738,9 @@ qlcnic_sysfs_clear_port_stats(struct file *file, struct kobject *kobj, struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; + if (QLCNIC_IS_83XX(adapter)) + return QL_STATUS_UNSUPPORTED_CMD; + if (offset >= QLCNIC_MAX_PCI_FUNC) return QL_STATUS_INVALID_PARAM; -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists