qlcnic: flash template based firmware reset recovery
authorSony Chacko <sony.chacko@qlogic.com>
Tue, 1 Jan 2013 03:20:25 +0000 (03:20 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Jan 2013 10:43:27 +0000 (02:43 -0800)
Flash template provides instructions to stop, restart and initalize the
firmware. These instructions are abstracted as a series of read, write and
poll operations on hardware registers. Register information and operation
specifics are not exposed to the driver. Driver reads the template from
flash and executes the instructions located at pre-defined offsets.

Template based firmware reset recovery and initialization mechanism minimize
driver changes as firmware evolves.

Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c

index 9a6d093441fad944ae92fb9a8b25de626849dab0..5b36d92b237d613d2a78f1eff69c2599fc299fb4 100644 (file)
@@ -442,6 +442,7 @@ struct qlcnic_hardware_context {
        struct qlcnic_nic_intr_coalesce coal;
        struct qlcnic_fw_dump fw_dump;
        struct qlcnic_fdt fdt;
+       struct qlc_83xx_reset reset;
        struct qlc_83xx_idc idc;
        struct qlc_83xx_fw_info fw_info;
        struct qlcnic_intrpt_config *intr_tbl;
index 8fdc4e6810285229a252358bd7decccfb32c1466..9cd51cbaecbc1b46811aac1e91d9bc0176be0a1c 100644 (file)
@@ -2343,3 +2343,43 @@ int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
 
        return ret;
 }
+
+int qlcnic_83xx_flash_read32(struct qlcnic_adapter *adapter, u32 flash_addr,
+                            u8 *p_data, int count)
+{
+       int i, ret;
+       u32 word, addr = flash_addr;
+       ulong  indirect_addr;
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0)
+               return -EIO;
+
+       if (addr & 0x3) {
+               dev_err(&adapter->pdev->dev, "Illegal addr = 0x%x\n", addr);
+               qlcnic_83xx_unlock_flash(adapter);
+               return -EIO;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (qlcnic_83xx_wrt_reg_indirect(adapter,
+                                                QLC_83XX_FLASH_DIRECT_WINDOW,
+                                                (addr))) {
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+
+               indirect_addr = QLC_83XX_FLASH_DIRECT_DATA(addr);
+               ret = qlcnic_83xx_rd_reg_indirect(adapter,
+                                                 indirect_addr);
+               if (ret == -EIO)
+                       return -EIO;
+               word = ret;
+               *p_data  = word;
+               p_data = p_data + 4;
+               addr = addr + 4;
+       }
+
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
index 99b84277b86f52bcf4177c5f234848965fecdbf9..969bda8e32c4e194cc802d547cebad87d858467a 100644 (file)
@@ -77,6 +77,8 @@
 #define QLC_83XX_BOOT_FROM_FLASH       0
 #define QLC_83XX_BOOT_FROM_FILE                0x12345678
 
+#define QLC_83XX_MAX_RESET_SEQ_ENTRIES 16
+
 struct qlcnic_intrpt_config {
        u8      type;
        u8      enabled;
@@ -98,6 +100,20 @@ struct qlc_83xx_fw_info {
        u8      load_from_file;
 };
 
+struct qlc_83xx_reset {
+       struct qlc_83xx_reset_hdr *hdr;
+       int     seq_index;
+       int     seq_error;
+       int     array_index;
+       u32     array[QLC_83XX_MAX_RESET_SEQ_ENTRIES];
+       u8      *buff;
+       u8      *stop_offset;
+       u8      *start_offset;
+       u8      *init_offset;
+       u8      seq_end;
+       u8      template_end;
+};
+
 #define QLC_83XX_IDC_DISABLE_FW_RESET_RECOVERY         0x1
 #define QLC_83XX_IDC_GRACEFULL_RESET                   0x2
 #define QLC_83XX_IDC_TIMESTAMP                         0
@@ -377,6 +393,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *);
 int qlcnic_83xx_idc_ready_state_entry(struct qlcnic_adapter *);
 int qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev);
 void qlcnic_83xx_idc_poll_dev_state(struct work_struct *);
+int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *);
 void qlcnic_83xx_idc_exit(struct qlcnic_adapter *);
 void qlcnic_83xx_idc_request_reset(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_lock_driver(struct qlcnic_adapter *);
index c26d1901900a5711b779836787d3a666b807540a..d222ee291e7c2711b5fc9e23d359dd6dad3e4aa6 100644 (file)
@@ -1,11 +1,77 @@
 #include "qlcnic.h"
 #include "qlcnic_hw.h"
 
+/* Reset template definitions */
+#define QLC_83XX_RESTART_TEMPLATE_SIZE         0x2000
+#define QLC_83XX_RESET_TEMPLATE_ADDR           0x4F0000
+#define QLC_83XX_RESET_SEQ_VERSION             0x0101
+
+#define QLC_83XX_OPCODE_NOP                    0x0000
+#define QLC_83XX_OPCODE_WRITE_LIST             0x0001
+#define QLC_83XX_OPCODE_READ_WRITE_LIST                0x0002
+#define QLC_83XX_OPCODE_POLL_LIST              0x0004
+#define QLC_83XX_OPCODE_POLL_WRITE_LIST                0x0008
+#define QLC_83XX_OPCODE_READ_MODIFY_WRITE      0x0010
+#define QLC_83XX_OPCODE_SEQ_PAUSE              0x0020
+#define QLC_83XX_OPCODE_SEQ_END                        0x0040
+#define QLC_83XX_OPCODE_TMPL_END               0x0080
+#define QLC_83XX_OPCODE_POLL_READ_LIST         0x0100
+
 static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter);
 static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
 static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);
 static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);
 
+/* Template header */
+struct qlc_83xx_reset_hdr {
+       u16     version;
+       u16     signature;
+       u16     size;
+       u16     entries;
+       u16     hdr_size;
+       u16     checksum;
+       u16     init_offset;
+       u16     start_offset;
+} __packed;
+
+/* Command entry header. */
+struct qlc_83xx_entry_hdr {
+       u16 cmd;
+       u16 size;
+       u16 count;
+       u16 delay;
+} __packed;
+
+/* Generic poll command */
+struct qlc_83xx_poll {
+       u32     mask;
+       u32     status;
+} __packed;
+
+/* Read modify write command */
+struct qlc_83xx_rmw {
+       u32     mask;
+       u32     xor_value;
+       u32     or_value;
+       u8      shl;
+       u8      shr;
+       u8      index_a;
+       u8      rsvd;
+} __packed;
+
+/* Generic command with 2 DWORD */
+struct qlc_83xx_entry {
+       u32 arg1;
+       u32 arg2;
+} __packed;
+
+/* Generic command with 4 DWORD */
+struct qlc_83xx_quad_entry {
+       u32 dr_addr;
+       u32 dr_value;
+       u32 ar_addr;
+       u32 ar_value;
+} __packed;
 static const char *const qlc_83xx_idc_states[] = {
        "Unknown",
        "Cold",
@@ -961,8 +1027,13 @@ qlcnic_83xx_idc_first_to_load_function_handler(struct qlcnic_adapter *adapter)
 
 static int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter)
 {
+       int ret = -EIO;
+
        qlcnic_83xx_setup_idc_parameters(adapter);
 
+       if (qlcnic_83xx_get_reset_instruction_template(adapter))
+               return ret;
+
        if (!qlcnic_83xx_idc_check_driver_presence_reg(adapter)) {
                if (qlcnic_83xx_idc_first_to_load_function_handler(adapter))
                        return -EIO;
@@ -1190,6 +1261,7 @@ static void qlcnic_83xx_dump_pause_control_regs(struct qlcnic_adapter *adapter)
                 val, val1);
 }
 
+
 static void qlcnic_83xx_disable_pause_frames(struct qlcnic_adapter *adapter)
 {
        u32 reg = 0, i, j;
@@ -1305,6 +1377,409 @@ int qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev)
        return err;
 }
 
+static int qlcnic_83xx_poll_reg(struct qlcnic_adapter *p_dev, u32 addr,
+                               int duration, u32 mask, u32 status)
+{
+       u32 value;
+       int timeout_error;
+       u8 retries;
+
+       value = qlcnic_83xx_rd_reg_indirect(p_dev, addr);
+       retries = duration / 10;
+
+       do {
+               if ((value & mask) != status) {
+                       timeout_error = 1;
+                       msleep(duration / 10);
+                       value = qlcnic_83xx_rd_reg_indirect(p_dev, addr);
+               } else {
+                       timeout_error = 0;
+                       break;
+               }
+       } while (retries--);
+
+       if (timeout_error) {
+               p_dev->ahw->reset.seq_error++;
+               dev_err(&p_dev->pdev->dev,
+                       "%s: Timeout Err, entry_num = %d\n",
+                       __func__, p_dev->ahw->reset.seq_index);
+               dev_err(&p_dev->pdev->dev,
+                       "0x%08x 0x%08x 0x%08x\n",
+                       value, mask, status);
+       }
+
+       return timeout_error;
+}
+
+static int qlcnic_83xx_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, count;
+       struct qlcnic_hardware_context *ahw = p_dev->ahw;
+
+       ahw->reset.seq_error = 0;
+       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;
+       count = sizeof(struct qlc_83xx_reset_hdr) / sizeof(u32);
+
+       /* Copy template header from flash */
+       if (qlcnic_83xx_flash_read32(p_dev, addr, p_buff, count)) {
+               dev_err(&p_dev->pdev->dev, "%s: flash read failed\n", __func__);
+               return -EIO;
+       }
+       ahw->reset.hdr = (struct qlc_83xx_reset_hdr *)ahw->reset.buff;
+       addr = QLC_83XX_RESET_TEMPLATE_ADDR + ahw->reset.hdr->hdr_size;
+       p_buff = ahw->reset.buff + ahw->reset.hdr->hdr_size;
+       count = (ahw->reset.hdr->size - ahw->reset.hdr->hdr_size) / sizeof(u32);
+
+       /* Copy rest of the template */
+       if (qlcnic_83xx_flash_read32(p_dev, addr, p_buff, count)) {
+               dev_err(&p_dev->pdev->dev, "%s: flash read failed\n", __func__);
+               return -EIO;
+       }
+
+       if (qlcnic_83xx_reset_template_checksum(p_dev))
+               return -EIO;
+       /* Get Stop, Start and Init command offsets */
+       ahw->reset.init_offset = ahw->reset.buff + ahw->reset.hdr->init_offset;
+       ahw->reset.start_offset = ahw->reset.buff +
+                                 ahw->reset.hdr->start_offset;
+       ahw->reset.stop_offset = ahw->reset.buff + ahw->reset.hdr->hdr_size;
+       return 0;
+}
+
+/* Read Write HW register command */
+static void qlcnic_83xx_read_write_crb_reg(struct qlcnic_adapter *p_dev,
+                                          u32 raddr, u32 waddr)
+{
+       int value;
+
+       value = qlcnic_83xx_rd_reg_indirect(p_dev, raddr);
+       qlcnic_83xx_wrt_reg_indirect(p_dev, waddr, value);
+}
+
+/* Read Modify Write HW register command */
+static void qlcnic_83xx_rmw_crb_reg(struct qlcnic_adapter *p_dev,
+                                   u32 raddr, u32 waddr,
+                                   struct qlc_83xx_rmw *p_rmw_hdr)
+{
+       int value;
+
+       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);
+
+       value &= p_rmw_hdr->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);
+}
+
+/* Write HW register command */
+static void qlcnic_83xx_write_list(struct qlcnic_adapter *p_dev,
+                                  struct qlc_83xx_entry_hdr *p_hdr)
+{
+       int i;
+       struct qlc_83xx_entry *entry;
+
+       entry = (struct qlc_83xx_entry *)((char *)p_hdr +
+                                         sizeof(struct qlc_83xx_entry_hdr));
+
+       for (i = 0; i < p_hdr->count; i++, entry++) {
+               qlcnic_83xx_wrt_reg_indirect(p_dev, entry->arg1,
+                                            entry->arg2);
+               if (p_hdr->delay)
+                       udelay((u32)(p_hdr->delay));
+       }
+}
+
+/* Read and Write instruction */
+static void qlcnic_83xx_read_write_list(struct qlcnic_adapter *p_dev,
+                                       struct qlc_83xx_entry_hdr *p_hdr)
+{
+       int i;
+       struct qlc_83xx_entry *entry;
+
+       entry = (struct qlc_83xx_entry *)((char *)p_hdr +
+                                         sizeof(struct qlc_83xx_entry_hdr));
+
+       for (i = 0; i < p_hdr->count; i++, entry++) {
+               qlcnic_83xx_read_write_crb_reg(p_dev, entry->arg1,
+                                              entry->arg2);
+               if (p_hdr->delay)
+                       udelay((u32)(p_hdr->delay));
+       }
+}
+
+/* Poll HW register command */
+static void qlcnic_83xx_poll_list(struct qlcnic_adapter *p_dev,
+                                 struct qlc_83xx_entry_hdr *p_hdr)
+{
+       long delay;
+       struct qlc_83xx_entry *entry;
+       struct qlc_83xx_poll *poll;
+       int i;
+       unsigned long arg1, arg2;
+
+       poll = (struct qlc_83xx_poll *)((char *)p_hdr +
+                                       sizeof(struct qlc_83xx_entry_hdr));
+
+       entry = (struct qlc_83xx_entry *)((char *)poll +
+                                         sizeof(struct qlc_83xx_poll));
+       delay = (long)p_hdr->delay;
+
+       if (!delay) {
+               for (i = 0; i < p_hdr->count; i++, entry++)
+                       qlcnic_83xx_poll_reg(p_dev, entry->arg1,
+                                            delay, poll->mask,
+                                            poll->status);
+       } else {
+               for (i = 0; i < p_hdr->count; i++, entry++) {
+                       arg1 = entry->arg1;
+                       arg2 = entry->arg2;
+                       if (delay) {
+                               if (qlcnic_83xx_poll_reg(p_dev,
+                                                        arg1, delay,
+                                                        poll->mask,
+                                                        poll->status)){
+                                       qlcnic_83xx_rd_reg_indirect(p_dev,
+                                                                   arg1);
+                                       qlcnic_83xx_rd_reg_indirect(p_dev,
+                                                                   arg2);
+                               }
+                       }
+               }
+       }
+}
+
+/* Poll and write HW register command */
+static void qlcnic_83xx_poll_write_list(struct qlcnic_adapter *p_dev,
+                                       struct qlc_83xx_entry_hdr *p_hdr)
+{
+       int i;
+       long delay;
+       struct qlc_83xx_quad_entry *entry;
+       struct qlc_83xx_poll *poll;
+
+       poll = (struct qlc_83xx_poll *)((char *)p_hdr +
+                                       sizeof(struct qlc_83xx_entry_hdr));
+       entry = (struct qlc_83xx_quad_entry *)((char *)poll +
+                                              sizeof(struct qlc_83xx_poll));
+       delay = (long)p_hdr->delay;
+
+       for (i = 0; i < p_hdr->count; i++, entry++) {
+               qlcnic_83xx_wrt_reg_indirect(p_dev, entry->dr_addr,
+                                            entry->dr_value);
+               qlcnic_83xx_wrt_reg_indirect(p_dev, entry->ar_addr,
+                                            entry->ar_value);
+               if (delay)
+                       qlcnic_83xx_poll_reg(p_dev, entry->ar_addr, delay,
+                                            poll->mask, poll->status);
+       }
+}
+
+/* Read Modify Write register command */
+static void qlcnic_83xx_read_modify_write(struct qlcnic_adapter *p_dev,
+                                         struct qlc_83xx_entry_hdr *p_hdr)
+{
+       int i;
+       struct qlc_83xx_entry *entry;
+       struct qlc_83xx_rmw *rmw_hdr;
+
+       rmw_hdr = (struct qlc_83xx_rmw *)((char *)p_hdr +
+                                         sizeof(struct qlc_83xx_entry_hdr));
+
+       entry = (struct qlc_83xx_entry *)((char *)rmw_hdr +
+                                         sizeof(struct qlc_83xx_rmw));
+
+       for (i = 0; i < p_hdr->count; i++, entry++) {
+               qlcnic_83xx_rmw_crb_reg(p_dev, entry->arg1,
+                                       entry->arg2, rmw_hdr);
+               if (p_hdr->delay)
+                       udelay((u32)(p_hdr->delay));
+       }
+}
+
+static void qlcnic_83xx_pause(struct qlc_83xx_entry_hdr *p_hdr)
+{
+       if (p_hdr->delay)
+               mdelay((u32)((long)p_hdr->delay));
+}
+
+/* Read and poll register command */
+static void qlcnic_83xx_poll_read_list(struct qlcnic_adapter *p_dev,
+                                      struct qlc_83xx_entry_hdr *p_hdr)
+{
+       long delay;
+       int index, i, j;
+       struct qlc_83xx_quad_entry *entry;
+       struct qlc_83xx_poll *poll;
+       unsigned long addr;
+
+       poll = (struct qlc_83xx_poll *)((char *)p_hdr +
+                                       sizeof(struct qlc_83xx_entry_hdr));
+
+       entry = (struct qlc_83xx_quad_entry *)((char *)poll +
+                                              sizeof(struct qlc_83xx_poll));
+       delay = (long)p_hdr->delay;
+
+       for (i = 0; i < p_hdr->count; i++, entry++) {
+               qlcnic_83xx_wrt_reg_indirect(p_dev, entry->ar_addr,
+                                            entry->ar_value);
+               if (delay) {
+                       if (!qlcnic_83xx_poll_reg(p_dev, entry->ar_addr, delay,
+                                                 poll->mask, poll->status)){
+                               index = p_dev->ahw->reset.array_index;
+                               addr = entry->dr_addr;
+                               j = qlcnic_83xx_rd_reg_indirect(p_dev, addr);
+                               p_dev->ahw->reset.array[index++] = j;
+
+                               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)
+{
+       p_dev->ahw->reset.seq_end = 1;
+}
+
+static void qlcnic_83xx_template_end(struct qlcnic_adapter *p_dev)
+{
+       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");
+}
+
+/**
+* qlcnic_83xx_exec_template_cmd
+*
+* @p_dev: adapter structure
+* @p_buff: Poiter to instruction template
+*
+* Template provides instructions to stop, restart and initalize firmware.
+* These instructions are abstracted as a series of read, write and
+* poll operations on hardware registers. Register information and operation
+* specifics are not exposed to the driver. Driver reads the template from
+* flash and executes the instructions located at pre-defined offsets.
+*
+* Returns: None
+* */
+static void qlcnic_83xx_exec_template_cmd(struct qlcnic_adapter *p_dev,
+                                         char *p_buff)
+{
+       int index, entries;
+       struct qlc_83xx_entry_hdr *p_hdr;
+       char *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 qlc_83xx_entry_hdr *)entry;
+
+               switch (p_hdr->cmd) {
+               case QLC_83XX_OPCODE_NOP:
+                       break;
+               case QLC_83XX_OPCODE_WRITE_LIST:
+                       qlcnic_83xx_write_list(p_dev, p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_READ_WRITE_LIST:
+                       qlcnic_83xx_read_write_list(p_dev, p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_POLL_LIST:
+                       qlcnic_83xx_poll_list(p_dev, p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_POLL_WRITE_LIST:
+                       qlcnic_83xx_poll_write_list(p_dev, p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_READ_MODIFY_WRITE:
+                       qlcnic_83xx_read_modify_write(p_dev, p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_SEQ_PAUSE:
+                       qlcnic_83xx_pause(p_hdr);
+                       break;
+               case QLC_83XX_OPCODE_SEQ_END:
+                       qlcnic_83xx_seq_end(p_dev);
+                       break;
+               case QLC_83XX_OPCODE_TMPL_END:
+                       qlcnic_83xx_template_end(p_dev);
+                       break;
+               case QLC_83XX_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 %d\n",
+                               __func__, p_hdr->cmd, index);
+                       break;
+               }
+               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_exec_template_cmd(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_exec_template_cmd(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_exec_template_cmd(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__);
+}
+
 static int qlcnic_83xx_load_fw_image_from_host(struct qlcnic_adapter *adapter)
 {
        int err = -EIO;
@@ -1329,6 +1804,9 @@ static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter)
 {
        int err = -EIO;
 
+       qlcnic_83xx_stop_hw(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 */
@@ -1340,6 +1818,7 @@ static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter)
                                    QLC_83XX_BOOT_FROM_FLASH);
        }
 
+       qlcnic_83xx_start_hw(adapter);
        if (qlcnic_83xx_check_hw_status(adapter))
                return -EIO;
 
index c10670f6d1c87c290deac7ffb37ea6eadcf71d1e..c71e1dc7295da21ddfbdb4bc997520ac8ee2b38f 100644 (file)
@@ -1473,6 +1473,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;
 }