Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/qla2xxx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 106 kB image not shown  

Quelle  qla_nx2.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * QLogic Fibre Channel HBA Driver
 * Copyright (c)  2003-2014 QLogic Corporation
 */


#include <linux/vmalloc.h>
#include <linux/delay.h>

#include "qla_def.h"
#include "qla_gbl.h"

#define TIMEOUT_100_MS 100

static const uint32_t qla8044_reg_tbl[] = {
 QLA8044_PEG_HALT_STATUS1,
 QLA8044_PEG_HALT_STATUS2,
 QLA8044_PEG_ALIVE_COUNTER,
 QLA8044_CRB_DRV_ACTIVE,
 QLA8044_CRB_DEV_STATE,
 QLA8044_CRB_DRV_STATE,
 QLA8044_CRB_DRV_SCRATCH,
 QLA8044_CRB_DEV_PART_INFO1,
 QLA8044_CRB_IDC_VER_MAJOR,
 QLA8044_FW_VER_MAJOR,
 QLA8044_FW_VER_MINOR,
 QLA8044_FW_VER_SUB,
 QLA8044_CMDPEG_STATE,
 QLA8044_ASIC_TEMP,
};

/* 8044 Flash Read/Write functions */
uint32_t
qla8044_rd_reg(struct qla_hw_data *ha, ulong addr)
{
 return readl((void __iomem *) (ha->nx_pcibase + addr));
}

void
qla8044_wr_reg(struct qla_hw_data *ha, ulong addr, uint32_t val)
{
 writel(val, (void __iomem *)((ha)->nx_pcibase + addr));
}

int
qla8044_rd_direct(struct scsi_qla_host *vha,
 const uint32_t crb_reg)
{
 struct qla_hw_data *ha = vha->hw;

 if (crb_reg < CRB_REG_INDEX_MAX)
  return qla8044_rd_reg(ha, qla8044_reg_tbl[crb_reg]);
 else
  return QLA_FUNCTION_FAILED;
}

void
qla8044_wr_direct(struct scsi_qla_host *vha,
 const uint32_t crb_reg,
 const uint32_t value)
{
 struct qla_hw_data *ha = vha->hw;

 if (crb_reg < CRB_REG_INDEX_MAX)
  qla8044_wr_reg(ha, qla8044_reg_tbl[crb_reg], value);
}

static int
qla8044_set_win_base(scsi_qla_host_t *vha, uint32_t addr)
{
 uint32_t val;
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 qla8044_wr_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum), addr);
 val = qla8044_rd_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum));

 if (val != addr) {
  ql_log(ql_log_warn, vha, 0xb087,
      "%s: Failed to set register window : "
      "addr written 0x%x, read 0x%x!\n",
      __func__, addr, val);
  ret_val = QLA_FUNCTION_FAILED;
 }
 return ret_val;
}

static int
qla8044_rd_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data)
{
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 ret_val = qla8044_set_win_base(vha, addr);
 if (!ret_val)
  *data = qla8044_rd_reg(ha, QLA8044_WILDCARD);
 else
  ql_log(ql_log_warn, vha, 0xb088,
      "%s: failed read of addr 0x%x!\n", __func__, addr);
 return ret_val;
}

static int
qla8044_wr_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t data)
{
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 ret_val = qla8044_set_win_base(vha, addr);
 if (!ret_val)
  qla8044_wr_reg(ha, QLA8044_WILDCARD, data);
 else
  ql_log(ql_log_warn, vha, 0xb089,
      "%s: failed wrt to addr 0x%x, data 0x%x\n",
      __func__, addr, data);
 return ret_val;
}

/*
 * qla8044_read_write_crb_reg - Read from raddr and write value to waddr.
 *
 * @ha : Pointer to adapter structure
 * @raddr : CRB address to read from
 * @waddr : CRB address to write to
 *
 */

static void
qla8044_read_write_crb_reg(struct scsi_qla_host *vha,
 uint32_t raddr, uint32_t waddr)
{
 uint32_t value;

 qla8044_rd_reg_indirect(vha, raddr, &value);
 qla8044_wr_reg_indirect(vha, waddr, value);
}

static int
qla8044_poll_wait_for_ready(struct scsi_qla_host *vha, uint32_t addr1,
 uint32_t mask)
{
 unsigned long timeout;
 uint32_t temp = 0;

 /* jiffies after 100ms */
 timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS);
 do {
  qla8044_rd_reg_indirect(vha, addr1, &temp);
  if ((temp & mask) != 0)
   break;
  if (time_after_eq(jiffies, timeout)) {
   ql_log(ql_log_warn, vha, 0xb151,
    "Error in processing rdmdio entry\n");
   return -1;
  }
 } while (1);

 return 0;
}

static uint32_t
qla8044_ipmdio_rd_reg(struct scsi_qla_host *vha,
 uint32_t addr1, uint32_t addr3, uint32_t mask, uint32_t addr)
{
 uint32_t temp;
 int ret = 0;

 ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
 if (ret == -1)
  return -1;

 temp = (0x40000000 | addr);
 qla8044_wr_reg_indirect(vha, addr1, temp);

 ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
 if (ret == -1)
  return 0;

 qla8044_rd_reg_indirect(vha, addr3, &ret);

 return ret;
}


static int
qla8044_poll_wait_ipmdio_bus_idle(struct scsi_qla_host *vha,
 uint32_t addr1, uint32_t addr2, uint32_t addr3, uint32_t mask)
{
 unsigned long timeout;
 uint32_t temp;

 /* jiffies after 100 msecs */
 timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS);
 do {
  temp = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr2);
  if ((temp & 0x1) != 1)
   break;
  if (time_after_eq(jiffies, timeout)) {
   ql_log(ql_log_warn, vha, 0xb152,
       "Error in processing mdiobus idle\n");
   return -1;
  }
 } while (1);

 return 0;
}

static int
qla8044_ipmdio_wr_reg(struct scsi_qla_host *vha, uint32_t addr1,
 uint32_t addr3, uint32_t mask, uint32_t addr, uint32_t value)
{
 int ret = 0;

 ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
 if (ret == -1)
  return -1;

 qla8044_wr_reg_indirect(vha, addr3, value);
 qla8044_wr_reg_indirect(vha, addr1, addr);

 ret = qla8044_poll_wait_for_ready(vha, addr1, mask);
 if (ret == -1)
  return -1;

 return 0;
}
/*
 * qla8044_rmw_crb_reg - Read value from raddr, AND with test_mask,
 * Shift Left,Right/OR/XOR with values RMW header and write value to waddr.
 *
 * @vha : Pointer to adapter structure
 * @raddr : CRB address to read from
 * @waddr : CRB address to write to
 * @p_rmw_hdr : header with shift/or/xor values.
 *
 */

static void
qla8044_rmw_crb_reg(struct scsi_qla_host *vha,
 uint32_t raddr, uint32_t waddr, struct qla8044_rmw *p_rmw_hdr)
{
 uint32_t value;

 if (p_rmw_hdr->index_a)
  value = vha->reset_tmplt.array[p_rmw_hdr->index_a];
 else
  qla8044_rd_reg_indirect(vha, raddr, &value);
 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;
 qla8044_wr_reg_indirect(vha, waddr, value);
 return;
}

static inline void
qla8044_set_qsnt_ready(struct scsi_qla_host *vha)
{
 uint32_t qsnt_state;
 struct qla_hw_data *ha = vha->hw;

 qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX);
 qsnt_state |= (1 << ha->portnum);
 qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state);
 ql_log(ql_log_info, vha, 0xb08e, "%s(%ld): qsnt_state: 0x%08x\n",
      __func__, vha->host_no, qsnt_state);
}

void
qla8044_clear_qsnt_ready(struct scsi_qla_host *vha)
{
 uint32_t qsnt_state;
 struct qla_hw_data *ha = vha->hw;

 qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX);
 qsnt_state &= ~(1 << ha->portnum);
 qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state);
 ql_log(ql_log_info, vha, 0xb08f, "%s(%ld): qsnt_state: 0x%08x\n",
     __func__, vha->host_no, qsnt_state);
}

/**
 * qla8044_lock_recovery - Recovers the idc_lock.
 * @vha : Pointer to adapter structure
 *
 * Lock Recovery Register
 * 5-2 Lock recovery owner: Function ID of driver doing lock recovery,
 * valid if bits 1..0 are set by driver doing lock recovery.
 * 1-0  1 - Driver intends to force unlock the IDC lock.
 * 2 - Driver is moving forward to unlock the IDC lock. Driver clears
 *     this field after force unlocking the IDC lock.
 *
 * Lock Recovery process
 * a. Read the IDC_LOCK_RECOVERY register. If the value in bits 1..0 is
 *    greater than 0, then wait for the other driver to unlock otherwise
 *    move to the next step.
 * b. Indicate intent to force-unlock by writing 1h to the IDC_LOCK_RECOVERY
 *    register bits 1..0 and also set the function# in bits 5..2.
 * c. Read the IDC_LOCK_RECOVERY register again after a delay of 200ms.
 *    Wait for the other driver to perform lock recovery if the function
 *    number in bits 5..2 has changed, otherwise move to the next step.
 * d. Write a value of 2h to the IDC_LOCK_RECOVERY register bits 1..0
 *    leaving your function# in bits 5..2.
 * e. Force unlock using the DRIVER_UNLOCK register and immediately clear
 *    the IDC_LOCK_RECOVERY bits 5..0 by writing 0.
 **/

static int
qla8044_lock_recovery(struct scsi_qla_host *vha)
{
 uint32_t lock = 0, lockid;
 struct qla_hw_data *ha = vha->hw;

 lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY);

 /* Check for other Recovery in progress, go wait */
 if ((lockid & IDC_LOCK_RECOVERY_STATE_MASK) != 0)
  return QLA_FUNCTION_FAILED;

 /* Intent to Recover */
 qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY,
     (ha->portnum <<
      IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) | INTENT_TO_RECOVER);
 msleep(200);

 /* Check Intent to Recover is advertised */
 lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY);
 if ((lockid & IDC_LOCK_RECOVERY_OWNER_MASK) != (ha->portnum <<
     IDC_LOCK_RECOVERY_STATE_SHIFT_BITS))
  return QLA_FUNCTION_FAILED;

 ql_dbg(ql_dbg_p3p, vha, 0xb08B, "%s:%d: IDC Lock recovery initiated\n"
     , __func__, ha->portnum);

 /* Proceed to Recover */
 qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY,
     (ha->portnum << IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) |
     PROCEED_TO_RECOVER);

 /* Force Unlock() */
 qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, 0xFF);
 qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK);

 /* Clear bits 0-5 in IDC_RECOVERY register*/
 qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, 0);

 /* Get lock() */
 lock = qla8044_rd_reg(ha, QLA8044_DRV_LOCK);
 if (lock) {
  lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID);
  lockid = ((lockid + (1 << 8)) & ~0xFF) | ha->portnum;
  qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lockid);
  return QLA_SUCCESS;
 } else
  return QLA_FUNCTION_FAILED;
}

int
qla8044_idc_lock(struct qla_hw_data *ha)
{
 uint32_t ret_val = QLA_SUCCESS, timeout = 0, status = 0;
 uint32_t lock_id, lock_cnt, func_num, tmo_owner = 0, first_owner = 0;
 scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);

 while (status == 0) {
  /* acquire semaphore5 from PCI HW block */
  status = qla8044_rd_reg(ha, QLA8044_DRV_LOCK);

  if (status) {
   /* Increment Counter (8-31) and update func_num (0-7) on
 * getting a successful lock  */

   lock_id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID);
   lock_id = ((lock_id + (1 << 8)) & ~0xFF) | ha->portnum;
   qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lock_id);
   break;
  }

  if (timeout == 0)
   first_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID);

  if (++timeout >=
      (QLA8044_DRV_LOCK_TIMEOUT / QLA8044_DRV_LOCK_MSLEEP)) {
   tmo_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID);
   func_num = tmo_owner & 0xFF;
   lock_cnt = tmo_owner >> 8;
   ql_log(ql_log_warn, vha, 0xb114,
       "%s: Lock by func %d failed after 2s, lock held "
       "by func %d, lock count %d, first_owner %d\n",
       __func__, ha->portnum, func_num, lock_cnt,
       (first_owner & 0xFF));
   if (first_owner != tmo_owner) {
    /* Some other driver got lock,
 * OR same driver got lock again (counter
 * value changed), when we were waiting for
 * lock. Retry for another 2 sec */

    ql_dbg(ql_dbg_p3p, vha, 0xb115,
        "%s: %d: IDC lock failed\n",
        __func__, ha->portnum);
    timeout = 0;
   } else {
    /* Same driver holding lock > 2sec.
 * Force Recovery */

    if (qla8044_lock_recovery(vha) == QLA_SUCCESS) {
     /* Recovered and got lock */
     ret_val = QLA_SUCCESS;
     ql_dbg(ql_dbg_p3p, vha, 0xb116,
         "%s:IDC lock Recovery by %d"
         "successful...\n", __func__,
          ha->portnum);
    }
    /* Recovery Failed, some other function
 * has the lock, wait for 2secs
 * and retry
 */

    ql_dbg(ql_dbg_p3p, vha, 0xb08a,
           "%s: IDC lock Recovery by %d "
           "failed, Retrying timeout\n", __func__,
           ha->portnum);
    timeout = 0;
   }
  }
  msleep(QLA8044_DRV_LOCK_MSLEEP);
 }
 return ret_val;
}

void
qla8044_idc_unlock(struct qla_hw_data *ha)
{
 int id;
 scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);

 id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID);

 if ((id & 0xFF) != ha->portnum) {
  ql_log(ql_log_warn, vha, 0xb118,
      "%s: IDC Unlock by %d failed, lock owner is %d!\n",
      __func__, ha->portnum, (id & 0xFF));
  return;
 }

 /* Keep lock counter value, update the ha->func_num to 0xFF */
 qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, (id | 0xFF));
 qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK);
}

/* 8044 Flash Lock/Unlock functions */
static int
qla8044_flash_lock(scsi_qla_host_t *vha)
{
 int lock_owner;
 int timeout = 0;
 uint32_t lock_status = 0;
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 while (lock_status == 0) {
  lock_status = qla8044_rd_reg(ha, QLA8044_FLASH_LOCK);
  if (lock_status)
   break;

  if (++timeout >= QLA8044_FLASH_LOCK_TIMEOUT / 20) {
   lock_owner = qla8044_rd_reg(ha,
       QLA8044_FLASH_LOCK_ID);
   ql_log(ql_log_warn, vha, 0xb113,
       "%s: Simultaneous flash access by following ports, active port = %d: accessing port = %d",
       __func__, ha->portnum, lock_owner);
   ret_val = QLA_FUNCTION_FAILED;
   break;
  }
  msleep(20);
 }
 qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, ha->portnum);
 return ret_val;
}

static void
qla8044_flash_unlock(scsi_qla_host_t *vha)
{
 struct qla_hw_data *ha = vha->hw;

 /* Reading FLASH_UNLOCK register unlocks the Flash */
 qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, 0xFF);
 qla8044_rd_reg(ha, QLA8044_FLASH_UNLOCK);
}


static
void qla8044_flash_lock_recovery(struct scsi_qla_host *vha)
{

 if (qla8044_flash_lock(vha)) {
  /* Someone else is holding the lock. */
  ql_log(ql_log_warn, vha, 0xb120, "Resetting flash_lock\n");
 }

 /*
 * Either we got the lock, or someone
 * else died while holding it.
 * In either case, unlock.
 */

 qla8044_flash_unlock(vha);
}

/*
 * Address and length are byte address
 */

static int
qla8044_read_flash_data(scsi_qla_host_t *vha,  uint8_t *p_data,
 uint32_t flash_addr, int u32_word_count)
{
 int i, ret_val = QLA_SUCCESS;
 uint32_t u32_word;

 if (qla8044_flash_lock(vha) != QLA_SUCCESS) {
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_lock_error;
 }

 if (flash_addr & 0x03) {
  ql_log(ql_log_warn, vha, 0xb117,
      "%s: Illegal addr = 0x%x\n", __func__, flash_addr);
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_flash_read;
 }

 for (i = 0; i < u32_word_count; i++) {
  if (qla8044_wr_reg_indirect(vha, QLA8044_FLASH_DIRECT_WINDOW,
      (flash_addr & 0xFFFF0000))) {
   ql_log(ql_log_warn, vha, 0xb119,
       "%s: failed to write addr 0x%x to "
       "FLASH_DIRECT_WINDOW\n! ",
       __func__, flash_addr);
   ret_val = QLA_FUNCTION_FAILED;
   goto exit_flash_read;
  }

  ret_val = qla8044_rd_reg_indirect(vha,
      QLA8044_FLASH_DIRECT_DATA(flash_addr),
      &u32_word);
  if (ret_val != QLA_SUCCESS) {
   ql_log(ql_log_warn, vha, 0xb08c,
       "%s: failed to read addr 0x%x!\n",
       __func__, flash_addr);
   goto exit_flash_read;
  }

  *(uint32_t *)p_data = u32_word;
  p_data = p_data + 4;
  flash_addr = flash_addr + 4;
 }

exit_flash_read:
 qla8044_flash_unlock(vha);

exit_lock_error:
 return ret_val;
}

/*
 * Address and length are byte address
 */

void *
qla8044_read_optrom_data(struct scsi_qla_host *vha, void *buf,
 uint32_t offset, uint32_t length)
{
 scsi_block_requests(vha->host);
 if (qla8044_read_flash_data(vha, buf, offset, length / 4)
     != QLA_SUCCESS) {
  ql_log(ql_log_warn, vha,  0xb08d,
      "%s: Failed to read from flash\n",
      __func__);
 }
 scsi_unblock_requests(vha->host);
 return buf;
}

static inline int
qla8044_need_reset(struct scsi_qla_host *vha)
{
 uint32_t drv_state, drv_active;
 int rval;
 struct qla_hw_data *ha = vha->hw;

 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);
 drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX);

 rval = drv_state & (1 << ha->portnum);

 if (ha->flags.eeh_busy && drv_active)
  rval = 1;
 return rval;
}

/*
 * qla8044_write_list - Write the value (p_entry->arg2) to address specified
 * by p_entry->arg1 for all entries in header with delay of p_hdr->delay between
 * entries.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : reset_entry header for WRITE_LIST opcode.
 *
 */

static void
qla8044_write_list(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 struct qla8044_entry *p_entry;
 uint32_t i;

 p_entry = (struct qla8044_entry *)((char *)p_hdr +
     sizeof(struct qla8044_reset_entry_hdr));

 for (i = 0; i < p_hdr->count; i++, p_entry++) {
  qla8044_wr_reg_indirect(vha, p_entry->arg1, p_entry->arg2);
  if (p_hdr->delay)
   udelay((uint32_t)(p_hdr->delay));
 }
}

/*
 * qla8044_read_write_list - Read from address specified by p_entry->arg1,
 * write value read to address specified by p_entry->arg2, for all entries in
 * header with delay of p_hdr->delay between entries.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : reset_entry header for READ_WRITE_LIST opcode.
 *
 */

static void
qla8044_read_write_list(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 struct qla8044_entry *p_entry;
 uint32_t i;

 p_entry = (struct qla8044_entry *)((char *)p_hdr +
     sizeof(struct qla8044_reset_entry_hdr));

 for (i = 0; i < p_hdr->count; i++, p_entry++) {
  qla8044_read_write_crb_reg(vha, p_entry->arg1,
      p_entry->arg2);
  if (p_hdr->delay)
   udelay((uint32_t)(p_hdr->delay));
 }
}

/*
 * qla8044_poll_reg - Poll the given CRB addr for duration msecs till
 * value read ANDed with test_mask is equal to test_result.
 *
 * @ha : Pointer to adapter structure
 * @addr : CRB register address
 * @duration : Poll for total of "duration" msecs
 * @test_mask : Mask value read with "test_mask"
 * @test_result : Compare (value&test_mask) with test_result.
 *
 * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED
 */

static int
qla8044_poll_reg(struct scsi_qla_host *vha, uint32_t addr,
 int duration, uint32_t test_mask, uint32_t test_result)
{
 uint32_t value = 0;
 int timeout_error;
 uint8_t retries;
 int ret_val = QLA_SUCCESS;

 ret_val = qla8044_rd_reg_indirect(vha, addr, &value);
 if (ret_val == QLA_FUNCTION_FAILED) {
  timeout_error = 1;
  goto exit_poll_reg;
 }

 /* poll every 1/10 of the total duration */
 retries = duration/10;

 do {
  if ((value & test_mask) != test_result) {
   timeout_error = 1;
   msleep(duration/10);
   ret_val = qla8044_rd_reg_indirect(vha, addr, &value);
   if (ret_val == QLA_FUNCTION_FAILED) {
    timeout_error = 1;
    goto exit_poll_reg;
   }
  } else {
   timeout_error = 0;
   break;
  }
 } while (retries--);

exit_poll_reg:
 if (timeout_error) {
  vha->reset_tmplt.seq_error++;
  ql_log(ql_log_fatal, vha, 0xb090,
      "%s: Poll Failed: 0x%08x 0x%08x 0x%08x\n",
      __func__, value, test_mask, test_result);
 }

 return timeout_error;
}

/*
 * qla8044_poll_list - For all entries in the POLL_LIST header, poll read CRB
 * register specified by p_entry->arg1 and compare (value AND test_mask) with
 * test_result to validate it. Wait for p_hdr->delay between processing entries.
 *
 * @ha : Pointer to adapter structure
 * @p_hdr : reset_entry header for POLL_LIST opcode.
 *
 */

static void
qla8044_poll_list(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 long delay;
 struct qla8044_entry *p_entry;
 struct qla8044_poll *p_poll;
 uint32_t i;
 uint32_t value;

 p_poll = (struct qla8044_poll *)
  ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr));

 /* Entries start after 8 byte qla8044_poll, poll header contains
 * the test_mask, test_value.
 */

 p_entry = (struct qla8044_entry *)((char *)p_poll +
     sizeof(struct qla8044_poll));

 delay = (long)p_hdr->delay;

 if (!delay) {
  for (i = 0; i < p_hdr->count; i++, p_entry++)
   qla8044_poll_reg(vha, 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 (qla8044_poll_reg(vha,
        p_entry->arg1, delay,
        p_poll->test_mask,
        p_poll->test_value)) {
     /*If
* (data_read&test_mask != test_value)
* read TIMEOUT_ADDR (arg1) and
* ADDR (arg2) registers
*/

     qla8044_rd_reg_indirect(vha,
         p_entry->arg1, &value);
     qla8044_rd_reg_indirect(vha,
         p_entry->arg2, &value);
    }
   }
  }
 }
}

/*
 * qla8044_poll_write_list - Write dr_value, ar_value to dr_addr/ar_addr,
 * read ar_addr, if (value& test_mask != test_mask) re-read till timeout
 * expires.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : reset entry header for POLL_WRITE_LIST opcode.
 *
 */

static void
qla8044_poll_write_list(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 long delay;
 struct qla8044_quad_entry *p_entry;
 struct qla8044_poll *p_poll;
 uint32_t i;

 p_poll = (struct qla8044_poll *)((char *)p_hdr +
     sizeof(struct qla8044_reset_entry_hdr));

 p_entry = (struct qla8044_quad_entry *)((char *)p_poll +
     sizeof(struct qla8044_poll));

 delay = (long)p_hdr->delay;

 for (i = 0; i < p_hdr->count; i++, p_entry++) {
  qla8044_wr_reg_indirect(vha,
      p_entry->dr_addr, p_entry->dr_value);
  qla8044_wr_reg_indirect(vha,
      p_entry->ar_addr, p_entry->ar_value);
  if (delay) {
   if (qla8044_poll_reg(vha,
       p_entry->ar_addr, delay,
       p_poll->test_mask,
       p_poll->test_value)) {
    ql_dbg(ql_dbg_p3p, vha, 0xb091,
        "%s: Timeout Error: poll list, ",
        __func__);
    ql_dbg(ql_dbg_p3p, vha, 0xb092,
        "item_num %d, entry_num %d\n", i,
        vha->reset_tmplt.seq_index);
   }
  }
 }
}

/*
 * qla8044_read_modify_write - Read value from p_entry->arg1, modify the
 * value, write value to p_entry->arg2. Process entries with p_hdr->delay
 * between entries.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : header with shift/or/xor values.
 *
 */

static void
qla8044_read_modify_write(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 struct qla8044_entry *p_entry;
 struct qla8044_rmw *p_rmw_hdr;
 uint32_t i;

 p_rmw_hdr = (struct qla8044_rmw *)((char *)p_hdr +
     sizeof(struct qla8044_reset_entry_hdr));

 p_entry = (struct qla8044_entry *)((char *)p_rmw_hdr +
     sizeof(struct qla8044_rmw));

 for (i = 0; i < p_hdr->count; i++, p_entry++) {
  qla8044_rmw_crb_reg(vha, p_entry->arg1,
      p_entry->arg2, p_rmw_hdr);
  if (p_hdr->delay)
   udelay((uint32_t)(p_hdr->delay));
 }
}

/*
 * qla8044_pause - Wait for p_hdr->delay msecs, called between processing
 * two entries of a sequence.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : Common reset entry header.
 *
 */

static
void qla8044_pause(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 if (p_hdr->delay)
  mdelay((uint32_t)((long)p_hdr->delay));
}

/*
 * qla8044_template_end - Indicates end of reset sequence processing.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : Common reset entry header.
 *
 */

static void
qla8044_template_end(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 vha->reset_tmplt.template_end = 1;

 if (vha->reset_tmplt.seq_error == 0) {
  ql_dbg(ql_dbg_p3p, vha, 0xb093,
      "%s: Reset sequence completed SUCCESSFULLY.\n", __func__);
 } else {
  ql_log(ql_log_fatal, vha, 0xb094,
      "%s: Reset sequence completed with some timeout "
      "errors.\n", __func__);
 }
}

/*
 * qla8044_poll_read_list - Write ar_value to ar_addr register, read ar_addr,
 * if (value & test_mask != test_value) re-read till timeout value expires,
 * read dr_addr register and assign to reset_tmplt.array.
 *
 * @vha : Pointer to adapter structure
 * @p_hdr : Common reset entry header.
 *
 */

static void
qla8044_poll_read_list(struct scsi_qla_host *vha,
 struct qla8044_reset_entry_hdr *p_hdr)
{
 long delay;
 int index;
 struct qla8044_quad_entry *p_entry;
 struct qla8044_poll *p_poll;
 uint32_t i;
 uint32_t value;

 p_poll = (struct qla8044_poll *)
  ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr));

 p_entry = (struct qla8044_quad_entry *)
  ((char *)p_poll + sizeof(struct qla8044_poll));

 delay = (long)p_hdr->delay;

 for (i = 0; i < p_hdr->count; i++, p_entry++) {
  qla8044_wr_reg_indirect(vha, p_entry->ar_addr,
      p_entry->ar_value);
  if (delay) {
   if (qla8044_poll_reg(vha, p_entry->ar_addr, delay,
       p_poll->test_mask, p_poll->test_value)) {
    ql_dbg(ql_dbg_p3p, vha, 0xb095,
        "%s: Timeout Error: poll "
        "list, ", __func__);
    ql_dbg(ql_dbg_p3p, vha, 0xb096,
        "Item_num %d, "
        "entry_num %d\n", i,
        vha->reset_tmplt.seq_index);
   } else {
    index = vha->reset_tmplt.array_index;
    qla8044_rd_reg_indirect(vha,
        p_entry->dr_addr, &value);
    vha->reset_tmplt.array[index++] = value;
    if (index == QLA8044_MAX_RESET_SEQ_ENTRIES)
     vha->reset_tmplt.array_index = 1;
   }
  }
 }
}

/*
 * qla8031_process_reset_template - Process all entries in reset template
 * till entry with SEQ_END opcode, which indicates end of the reset template
 * processing. Each entry has a Reset Entry header, entry opcode/command, with
 * size of the entry, number of entries in sub-sequence and delay in microsecs
 * or timeout in millisecs.
 *
 * @ha : Pointer to adapter structure
 * @p_buff : Common reset entry header.
 *
 */

static void
qla8044_process_reset_template(struct scsi_qla_host *vha,
 char *p_buff)
{
 int index, entries;
 struct qla8044_reset_entry_hdr *p_hdr;
 char *p_entry = p_buff;

 vha->reset_tmplt.seq_end = 0;
 vha->reset_tmplt.template_end = 0;
 entries = vha->reset_tmplt.hdr->entries;
 index = vha->reset_tmplt.seq_index;

 for (; (!vha->reset_tmplt.seq_end) && (index  < entries); index++) {
  p_hdr = (struct qla8044_reset_entry_hdr *)p_entry;
  switch (p_hdr->cmd) {
  case OPCODE_NOP:
   break;
  case OPCODE_WRITE_LIST:
   qla8044_write_list(vha, p_hdr);
   break;
  case OPCODE_READ_WRITE_LIST:
   qla8044_read_write_list(vha, p_hdr);
   break;
  case OPCODE_POLL_LIST:
   qla8044_poll_list(vha, p_hdr);
   break;
  case OPCODE_POLL_WRITE_LIST:
   qla8044_poll_write_list(vha, p_hdr);
   break;
  case OPCODE_READ_MODIFY_WRITE:
   qla8044_read_modify_write(vha, p_hdr);
   break;
  case OPCODE_SEQ_PAUSE:
   qla8044_pause(vha, p_hdr);
   break;
  case OPCODE_SEQ_END:
   vha->reset_tmplt.seq_end = 1;
   break;
  case OPCODE_TMPL_END:
   qla8044_template_end(vha, p_hdr);
   break;
  case OPCODE_POLL_READ_LIST:
   qla8044_poll_read_list(vha, p_hdr);
   break;
  default:
   ql_log(ql_log_fatal, vha, 0xb097,
       "%s: Unknown command ==> 0x%04x on "
       "entry = %d\n", __func__, p_hdr->cmd, index);
   break;
  }
  /*
 *Set pointer to next entry in the sequence.
*/

  p_entry += p_hdr->size;
 }
 vha->reset_tmplt.seq_index = index;
}

static void
qla8044_process_init_seq(struct scsi_qla_host *vha)
{
 qla8044_process_reset_template(vha,
     vha->reset_tmplt.init_offset);
 if (vha->reset_tmplt.seq_end != 1)
  ql_log(ql_log_fatal, vha, 0xb098,
      "%s: Abrupt INIT Sub-Sequence end.\n",
      __func__);
}

static void
qla8044_process_stop_seq(struct scsi_qla_host *vha)
{
 vha->reset_tmplt.seq_index = 0;
 qla8044_process_reset_template(vha, vha->reset_tmplt.stop_offset);
 if (vha->reset_tmplt.seq_end != 1)
  ql_log(ql_log_fatal, vha, 0xb099,
      "%s: Abrupt STOP Sub-Sequence end.\n", __func__);
}

static void
qla8044_process_start_seq(struct scsi_qla_host *vha)
{
 qla8044_process_reset_template(vha, vha->reset_tmplt.start_offset);
 if (vha->reset_tmplt.template_end != 1)
  ql_log(ql_log_fatal, vha, 0xb09a,
      "%s: Abrupt START Sub-Sequence end.\n",
      __func__);
}

static int
qla8044_lockless_flash_read_u32(struct scsi_qla_host *vha,
 uint32_t flash_addr, uint8_t *p_data, int u32_word_count)
{
 uint32_t i;
 uint32_t u32_word;
 uint32_t flash_offset;
 uint32_t addr = flash_addr;
 int ret_val = QLA_SUCCESS;

 flash_offset = addr & (QLA8044_FLASH_SECTOR_SIZE - 1);

 if (addr & 0x3) {
  ql_log(ql_log_fatal, vha, 0xb09b, "%s: Illegal addr = 0x%x\n",
      __func__, addr);
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_lockless_read;
 }

 ret_val = qla8044_wr_reg_indirect(vha,
     QLA8044_FLASH_DIRECT_WINDOW, (addr));

 if (ret_val != QLA_SUCCESS) {
  ql_log(ql_log_fatal, vha, 0xb09c,
      "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
      __func__, addr);
  goto exit_lockless_read;
 }

 /* Check if data is spread across multiple sectors  */
 if ((flash_offset + (u32_word_count * sizeof(uint32_t))) >
     (QLA8044_FLASH_SECTOR_SIZE - 1)) {
  /* Multi sector read */
  for (i = 0; i < u32_word_count; i++) {
   ret_val = qla8044_rd_reg_indirect(vha,
       QLA8044_FLASH_DIRECT_DATA(addr), &u32_word);
   if (ret_val != QLA_SUCCESS) {
    ql_log(ql_log_fatal, vha, 0xb09d,
        "%s: failed to read addr 0x%x!\n",
        __func__, addr);
    goto exit_lockless_read;
   }
   *(uint32_t *)p_data  = u32_word;
   p_data = p_data + 4;
   addr = addr + 4;
   flash_offset = flash_offset + 4;
   if (flash_offset > (QLA8044_FLASH_SECTOR_SIZE - 1)) {
    /* This write is needed once for each sector */
    ret_val = qla8044_wr_reg_indirect(vha,
        QLA8044_FLASH_DIRECT_WINDOW, (addr));
    if (ret_val != QLA_SUCCESS) {
     ql_log(ql_log_fatal, vha, 0xb09f,
         "%s: failed to write addr "
         "0x%x to FLASH_DIRECT_WINDOW!\n",
         __func__, addr);
     goto exit_lockless_read;
    }
    flash_offset = 0;
   }
  }
 } else {
  /* Single sector read */
  for (i = 0; i < u32_word_count; i++) {
   ret_val = qla8044_rd_reg_indirect(vha,
       QLA8044_FLASH_DIRECT_DATA(addr), &u32_word);
   if (ret_val != QLA_SUCCESS) {
    ql_log(ql_log_fatal, vha, 0xb0a0,
        "%s: failed to read addr 0x%x!\n",
        __func__, addr);
    goto exit_lockless_read;
   }
   *(uint32_t *)p_data = u32_word;
   p_data = p_data + 4;
   addr = addr + 4;
  }
 }

exit_lockless_read:
 return ret_val;
}

/*
 * qla8044_ms_mem_write_128b - Writes data to MS/off-chip memory
 *
 * @vha : Pointer to adapter structure
 * addr : Flash address to write to
 * data : Data to be written
 * count : word_count to be written
 *
 * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED
 */

static int
qla8044_ms_mem_write_128b(struct scsi_qla_host *vha,
 uint64_t addr, uint32_t *data, uint32_t count)
{
 int i, j, ret_val = QLA_SUCCESS;
 uint32_t agt_ctrl;
 unsigned long flags;
 struct qla_hw_data *ha = vha->hw;

 /* Only 128-bit aligned access */
 if (addr & 0xF) {
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_ms_mem_write;
 }
 write_lock_irqsave(&ha->hw_lock, flags);

 /* Write address */
 ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, 0);
 if (ret_val == QLA_FUNCTION_FAILED) {
  ql_log(ql_log_fatal, vha, 0xb0a1,
      "%s: write to AGT_ADDR_HI failed!\n", __func__);
  goto exit_ms_mem_write_unlock;
 }

 for (i = 0; i < count; i++, addr += 16) {
  if (!((addr_in_range(addr, QLA8044_ADDR_QDR_NET,
      QLA8044_ADDR_QDR_NET_MAX)) ||
      (addr_in_range(addr, QLA8044_ADDR_DDR_NET,
   QLA8044_ADDR_DDR_NET_MAX)))) {
   ret_val = QLA_FUNCTION_FAILED;
   goto exit_ms_mem_write_unlock;
  }

  ret_val = qla8044_wr_reg_indirect(vha,
      MD_MIU_TEST_AGT_ADDR_LO, addr);

  /* Write data */
  ret_val += qla8044_wr_reg_indirect(vha,
      MD_MIU_TEST_AGT_WRDATA_LO, *data++);
  ret_val += qla8044_wr_reg_indirect(vha,
      MD_MIU_TEST_AGT_WRDATA_HI, *data++);
  ret_val += qla8044_wr_reg_indirect(vha,
      MD_MIU_TEST_AGT_WRDATA_ULO, *data++);
  ret_val += qla8044_wr_reg_indirect(vha,
      MD_MIU_TEST_AGT_WRDATA_UHI, *data++);
  if (ret_val == QLA_FUNCTION_FAILED) {
   ql_log(ql_log_fatal, vha, 0xb0a2,
       "%s: write to AGT_WRDATA failed!\n",
       __func__);
   goto exit_ms_mem_write_unlock;
  }

  /* Check write status */
  ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL,
      MIU_TA_CTL_WRITE_ENABLE);
  ret_val += qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL,
      MIU_TA_CTL_WRITE_START);
  if (ret_val == QLA_FUNCTION_FAILED) {
   ql_log(ql_log_fatal, vha, 0xb0a3,
       "%s: write to AGT_CTRL failed!\n", __func__);
   goto exit_ms_mem_write_unlock;
  }

  for (j = 0; j < MAX_CTL_CHECK; j++) {
   ret_val = qla8044_rd_reg_indirect(vha,
       MD_MIU_TEST_AGT_CTRL, &agt_ctrl);
   if (ret_val == QLA_FUNCTION_FAILED) {
    ql_log(ql_log_fatal, vha, 0xb0a4,
        "%s: failed to read "
        "MD_MIU_TEST_AGT_CTRL!\n", __func__);
    goto exit_ms_mem_write_unlock;
   }
   if ((agt_ctrl & MIU_TA_CTL_BUSY) == 0)
    break;
  }

  /* Status check failed */
  if (j >= MAX_CTL_CHECK) {
   ql_log(ql_log_fatal, vha, 0xb0a5,
       "%s: MS memory write failed!\n",
      __func__);
   ret_val = QLA_FUNCTION_FAILED;
   goto exit_ms_mem_write_unlock;
  }
 }

exit_ms_mem_write_unlock:
 write_unlock_irqrestore(&ha->hw_lock, flags);

exit_ms_mem_write:
 return ret_val;
}

static int
qla8044_copy_bootloader(struct scsi_qla_host *vha)
{
 uint8_t *p_cache;
 uint32_t src, count, size;
 uint64_t dest;
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 src = QLA8044_BOOTLOADER_FLASH_ADDR;
 dest = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_ADDR);
 size = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_SIZE);

 /* 128 bit alignment check */
 if (size & 0xF)
  size = (size + 16) & ~0xF;

 /* 16 byte count */
 count = size/16;

 p_cache = vmalloc(size);
 if (p_cache == NULL) {
  ql_log(ql_log_fatal, vha, 0xb0a6,
      "%s: Failed to allocate memory for "
      "boot loader cache\n", __func__);
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_copy_bootloader;
 }

 ret_val = qla8044_lockless_flash_read_u32(vha, src,
     p_cache, size/sizeof(uint32_t));
 if (ret_val == QLA_FUNCTION_FAILED) {
  ql_log(ql_log_fatal, vha, 0xb0a7,
      "%s: Error reading F/W from flash!!!\n", __func__);
  goto exit_copy_error;
 }
 ql_dbg(ql_dbg_p3p, vha, 0xb0a8, "%s: Read F/W from flash!\n",
     __func__);

 /* 128 bit/16 byte write to MS memory */
 ret_val = qla8044_ms_mem_write_128b(vha, dest,
     (uint32_t *)p_cache, count);
 if (ret_val == QLA_FUNCTION_FAILED) {
  ql_log(ql_log_fatal, vha, 0xb0a9,
      "%s: Error writing F/W to MS !!!\n", __func__);
  goto exit_copy_error;
 }
 ql_dbg(ql_dbg_p3p, vha, 0xb0aa,
     "%s: Wrote F/W (size %d) to MS !!!\n",
     __func__, size);

exit_copy_error:
 vfree(p_cache);

exit_copy_bootloader:
 return ret_val;
}

static int
qla8044_restart(struct scsi_qla_host *vha)
{
 int ret_val = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 qla8044_process_stop_seq(vha);

 /* Collect minidump */
 if (ql2xmdenable)
  qla8044_get_minidump(vha);
 else
  ql_log(ql_log_fatal, vha, 0xb14c,
      "Minidump disabled.\n");

 qla8044_process_init_seq(vha);

 if (qla8044_copy_bootloader(vha)) {
  ql_log(ql_log_fatal, vha, 0xb0ab,
      "%s: Copy bootloader, firmware restart failed!\n",
      __func__);
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_restart;
 }

 /*
 *  Loads F/W from flash
 */

 qla8044_wr_reg(ha, QLA8044_FW_IMAGE_VALID, QLA8044_BOOT_FROM_FLASH);

 qla8044_process_start_seq(vha);

exit_restart:
 return ret_val;
}

/*
 * qla8044_check_cmd_peg_status - Check peg status to see if Peg is
 * initialized.
 *
 * @ha : Pointer to adapter structure
 *
 * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED
 */

static int
qla8044_check_cmd_peg_status(struct scsi_qla_host *vha)
{
 uint32_t val, ret_val = QLA_FUNCTION_FAILED;
 int retries = CRB_CMDPEG_CHECK_RETRY_COUNT;
 struct qla_hw_data *ha = vha->hw;

 do {
  val = qla8044_rd_reg(ha, QLA8044_CMDPEG_STATE);
  if (val == PHAN_INITIALIZE_COMPLETE) {
   ql_dbg(ql_dbg_p3p, vha, 0xb0ac,
       "%s: Command Peg initialization "
       "complete! state=0x%x\n", __func__, val);
   ret_val = QLA_SUCCESS;
   break;
  }
  msleep(CRB_CMDPEG_CHECK_DELAY);
 } while (--retries);

 return ret_val;
}

static int
qla8044_start_firmware(struct scsi_qla_host *vha)
{
 int ret_val = QLA_SUCCESS;

 if (qla8044_restart(vha)) {
  ql_log(ql_log_fatal, vha, 0xb0ad,
      "%s: Restart Error!!!, Need Reset!!!\n",
      __func__);
  ret_val = QLA_FUNCTION_FAILED;
  goto exit_start_fw;
 } else
  ql_dbg(ql_dbg_p3p, vha, 0xb0af,
      "%s: Restart done!\n", __func__);

 ret_val = qla8044_check_cmd_peg_status(vha);
 if (ret_val) {
  ql_log(ql_log_fatal, vha, 0xb0b0,
      "%s: Peg not initialized!\n", __func__);
  ret_val = QLA_FUNCTION_FAILED;
 }

exit_start_fw:
 return ret_val;
}

void
qla8044_clear_drv_active(struct qla_hw_data *ha)
{
 uint32_t drv_active;
 struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);

 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);
 drv_active &= ~(1 << (ha->portnum));

 ql_log(ql_log_info, vha, 0xb0b1,
     "%s(%ld): drv_active: 0x%08x\n",
     __func__, vha->host_no, drv_active);

 qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active);
}

/*
 * qla8044_device_bootstrap - Initialize device, set DEV_READY, start fw
 * @ha: pointer to adapter structure
 *
 * Note: IDC lock must be held upon entry
 **/

static int
qla8044_device_bootstrap(struct scsi_qla_host *vha)
{
 int rval = QLA_FUNCTION_FAILED;
 int i;
 uint32_t old_count = 0, count = 0;
 int need_reset = 0;
 uint32_t idc_ctrl;
 struct qla_hw_data *ha = vha->hw;

 need_reset = qla8044_need_reset(vha);

 if (!need_reset) {
  old_count = qla8044_rd_direct(vha,
      QLA8044_PEG_ALIVE_COUNTER_INDEX);

  for (i = 0; i < 10; i++) {
   msleep(200);

   count = qla8044_rd_direct(vha,
       QLA8044_PEG_ALIVE_COUNTER_INDEX);
   if (count != old_count) {
    rval = QLA_SUCCESS;
    goto dev_ready;
   }
  }
  qla8044_flash_lock_recovery(vha);
 } else {
  /* We are trying to perform a recovery here. */
  if (ha->flags.isp82xx_fw_hung)
   qla8044_flash_lock_recovery(vha);
 }

 /* set to DEV_INITIALIZING */
 ql_log(ql_log_info, vha, 0xb0b2,
     "%s: HW State: INITIALIZING\n", __func__);
 qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX,
     QLA8XXX_DEV_INITIALIZING);

 qla8044_idc_unlock(ha);
 rval = qla8044_start_firmware(vha);
 qla8044_idc_lock(ha);

 if (rval != QLA_SUCCESS) {
  ql_log(ql_log_info, vha, 0xb0b3,
       "%s: HW State: FAILED\n", __func__);
  qla8044_clear_drv_active(ha);
  qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX,
      QLA8XXX_DEV_FAILED);
  return rval;
 }

 /* For ISP8044, If IDC_CTRL GRACEFUL_RESET_BIT1 is set , reset it after
 * device goes to INIT state. */

 idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL);
 if (idc_ctrl & GRACEFUL_RESET_BIT1) {
  qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL,
      (idc_ctrl & ~GRACEFUL_RESET_BIT1));
  ha->fw_dumped = false;
 }

dev_ready:
 ql_log(ql_log_info, vha, 0xb0b4,
     "%s: HW State: READY\n", __func__);
 qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, QLA8XXX_DEV_READY);

 return rval;
}

/*-------------------------Reset Sequence Functions-----------------------*/
static void
qla8044_dump_reset_seq_hdr(struct scsi_qla_host *vha)
{
 u8 *phdr;

 if (!vha->reset_tmplt.buff) {
  ql_log(ql_log_fatal, vha, 0xb0b5,
      "%s: Error Invalid reset_seq_template\n", __func__);
  return;
 }

 phdr = vha->reset_tmplt.buff;
 ql_dbg(ql_dbg_p3p, vha, 0xb0b6,
     "Reset Template :\n\t0x%X 0x%X 0x%X 0x%X"
     "0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n"
     "\t0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n\n",
     *phdr, *(phdr+1), *(phdr+2), *(phdr+3), *(phdr+4),
     *(phdr+5), *(phdr+6), *(phdr+7), *(phdr + 8),
     *(phdr+9), *(phdr+10), *(phdr+11), *(phdr+12),
     *(phdr+13), *(phdr+14), *(phdr+15));
}

/*
 * qla8044_reset_seq_checksum_test - Validate Reset Sequence template.
 *
 * @ha : Pointer to adapter structure
 *
 * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED
 */

static int
qla8044_reset_seq_checksum_test(struct scsi_qla_host *vha)
{
 uint32_t sum =  0;
 uint16_t *buff = (uint16_t *)vha->reset_tmplt.buff;
 int u16_count =  vha->reset_tmplt.hdr->size / sizeof(uint16_t);

 while (u16_count-- > 0)
  sum += *buff++;

 while (sum >> 16)
  sum = (sum & 0xFFFF) +  (sum >> 16);

 /* checksum of 0 indicates a valid template */
 if (~sum) {
  return QLA_SUCCESS;
 } else {
  ql_log(ql_log_fatal, vha, 0xb0b7,
      "%s: Reset seq checksum failed\n", __func__);
  return QLA_FUNCTION_FAILED;
 }
}

/*
 * qla8044_read_reset_template - Read Reset Template from Flash, validate
 * the template and store offsets of stop/start/init offsets in ha->reset_tmplt.
 *
 * @ha : Pointer to adapter structure
 */

void
qla8044_read_reset_template(struct scsi_qla_host *vha)
{
 uint8_t *p_buff;
 uint32_t addr, tmplt_hdr_def_size, tmplt_hdr_size;

 vha->reset_tmplt.seq_error = 0;
 vha->reset_tmplt.buff = vmalloc(QLA8044_RESTART_TEMPLATE_SIZE);
 if (vha->reset_tmplt.buff == NULL) {
  ql_log(ql_log_fatal, vha, 0xb0b8,
      "%s: Failed to allocate reset template resources\n",
      __func__);
  goto exit_read_reset_template;
 }

 p_buff = vha->reset_tmplt.buff;
 addr = QLA8044_RESET_TEMPLATE_ADDR;

 tmplt_hdr_def_size =
     sizeof(struct qla8044_reset_template_hdr) / sizeof(uint32_t);

 ql_dbg(ql_dbg_p3p, vha, 0xb0b9,
     "%s: Read template hdr size %d from Flash\n",
     __func__, tmplt_hdr_def_size);

 /* Copy template header from flash */
 if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) {
  ql_log(ql_log_fatal, vha, 0xb0ba,
      "%s: Failed to read reset template\n", __func__);
  goto exit_read_template_error;
 }

 vha->reset_tmplt.hdr =
  (struct qla8044_reset_template_hdr *) vha->reset_tmplt.buff;

 /* Validate the template header size and signature */
 tmplt_hdr_size = vha->reset_tmplt.hdr->hdr_size/sizeof(uint32_t);
 if ((tmplt_hdr_size != tmplt_hdr_def_size) ||
     (vha->reset_tmplt.hdr->signature != RESET_TMPLT_HDR_SIGNATURE)) {
  ql_log(ql_log_fatal, vha, 0xb0bb,
      "%s: Template Header size invalid %d "
      "tmplt_hdr_def_size %d!!!\n", __func__,
      tmplt_hdr_size, tmplt_hdr_def_size);
  goto exit_read_template_error;
 }

 addr = QLA8044_RESET_TEMPLATE_ADDR + vha->reset_tmplt.hdr->hdr_size;
 p_buff = vha->reset_tmplt.buff + vha->reset_tmplt.hdr->hdr_size;
 tmplt_hdr_def_size = (vha->reset_tmplt.hdr->size -
     vha->reset_tmplt.hdr->hdr_size)/sizeof(uint32_t);

 ql_dbg(ql_dbg_p3p, vha, 0xb0bc,
     "%s: Read rest of the template size %d\n",
     __func__, vha->reset_tmplt.hdr->size);

 /* Copy rest of the template */
 if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) {
  ql_log(ql_log_fatal, vha, 0xb0bd,
      "%s: Failed to read reset template\n", __func__);
  goto exit_read_template_error;
 }

 /* Integrity check */
 if (qla8044_reset_seq_checksum_test(vha)) {
  ql_log(ql_log_fatal, vha, 0xb0be,
      "%s: Reset Seq checksum failed!\n", __func__);
  goto exit_read_template_error;
 }

 ql_dbg(ql_dbg_p3p, vha, 0xb0bf,
     "%s: Reset Seq checksum passed! Get stop, "
     "start and init seq offsets\n", __func__);

 /* Get STOP, START, INIT sequence offsets */
 vha->reset_tmplt.init_offset = vha->reset_tmplt.buff +
     vha->reset_tmplt.hdr->init_seq_offset;

 vha->reset_tmplt.start_offset = vha->reset_tmplt.buff +
     vha->reset_tmplt.hdr->start_seq_offset;

 vha->reset_tmplt.stop_offset = vha->reset_tmplt.buff +
     vha->reset_tmplt.hdr->hdr_size;

 qla8044_dump_reset_seq_hdr(vha);

 goto exit_read_reset_template;

exit_read_template_error:
 vfree(vha->reset_tmplt.buff);

exit_read_reset_template:
 return;
}

void
qla8044_set_idc_dontreset(struct scsi_qla_host *vha)
{
 uint32_t idc_ctrl;
 struct qla_hw_data *ha = vha->hw;

 idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL);
 idc_ctrl |= DONTRESET_BIT0;
 ql_dbg(ql_dbg_p3p, vha, 0xb0c0,
     "%s: idc_ctrl = %d\n", __func__, idc_ctrl);
 qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl);
}

static inline void
qla8044_set_rst_ready(struct scsi_qla_host *vha)
{
 uint32_t drv_state;
 struct qla_hw_data *ha = vha->hw;

 drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX);

 /* For ISP8044, drv_active register has 1 bit per function,
 * shift 1 by func_num to set a bit for the function.*/

 drv_state |= (1 << ha->portnum);

 ql_log(ql_log_info, vha, 0xb0c1,
     "%s(%ld): drv_state: 0x%08x\n",
     __func__, vha->host_no, drv_state);
 qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, drv_state);
}

/**
 * qla8044_need_reset_handler - Code to start reset sequence
 * @vha: pointer to adapter structure
 *
 * Note: IDC lock must be held upon entry
 */

static void
qla8044_need_reset_handler(struct scsi_qla_host *vha)
{
 uint32_t dev_state = 0, drv_state, drv_active;
 unsigned long reset_timeout;
 struct qla_hw_data *ha = vha->hw;

 ql_log(ql_log_fatal, vha, 0xb0c2,
     "%s: Performing ISP error recovery\n", __func__);

 if (vha->flags.online) {
  qla8044_idc_unlock(ha);
  qla2x00_abort_isp_cleanup(vha);
  ha->isp_ops->get_flash_version(vha, vha->req->ring);
  ha->isp_ops->nvram_config(vha);
  qla8044_idc_lock(ha);
 }

 dev_state = qla8044_rd_direct(vha,
     QLA8044_CRB_DEV_STATE_INDEX);
 drv_state = qla8044_rd_direct(vha,
     QLA8044_CRB_DRV_STATE_INDEX);
 drv_active = qla8044_rd_direct(vha,
     QLA8044_CRB_DRV_ACTIVE_INDEX);

 ql_log(ql_log_info, vha, 0xb0c5,
     "%s(%ld): drv_state = 0x%x, drv_active = 0x%x dev_state = 0x%x\n",
     __func__, vha->host_no, drv_state, drv_active, dev_state);

 qla8044_set_rst_ready(vha);

 /* wait for 10 seconds for reset ack from all functions */
 reset_timeout = jiffies + (ha->fcoe_reset_timeout * HZ);

 do {
  if (time_after_eq(jiffies, reset_timeout)) {
   ql_log(ql_log_info, vha, 0xb0c4,
       "%s: Function %d: Reset Ack Timeout!, drv_state: 0x%08x, drv_active: 0x%08x\n",
       __func__, ha->portnum, drv_state, drv_active);
   break;
  }

  qla8044_idc_unlock(ha);
  msleep(1000);
  qla8044_idc_lock(ha);

  dev_state = qla8044_rd_direct(vha,
      QLA8044_CRB_DEV_STATE_INDEX);
  drv_state = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_STATE_INDEX);
  drv_active = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_ACTIVE_INDEX);
 } while (((drv_state & drv_active) != drv_active) &&
     (dev_state == QLA8XXX_DEV_NEED_RESET));

 /* Remove IDC participation of functions not acknowledging */
 if (drv_state != drv_active) {
  ql_log(ql_log_info, vha, 0xb0c7,
      "%s(%ld): Function %d turning off drv_active of non-acking function 0x%x\n",
      __func__, vha->host_no, ha->portnum,
      (drv_active ^ drv_state));
  drv_active = drv_active & drv_state;
  qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX,
      drv_active);
 } else {
  /*
 * Reset owner should execute reset recovery,
 * if all functions acknowledged
 */

  if ((ha->flags.nic_core_reset_owner) &&
      (dev_state == QLA8XXX_DEV_NEED_RESET)) {
   ha->flags.nic_core_reset_owner = 0;
   qla8044_device_bootstrap(vha);
   return;
  }
 }

 /* Exit if non active function */
 if (!(drv_active & (1 << ha->portnum))) {
  ha->flags.nic_core_reset_owner = 0;
  return;
 }

 /*
 * Execute Reset Recovery if Reset Owner or Function 7
 * is the only active function
 */

 if (ha->flags.nic_core_reset_owner ||
     ((drv_state & drv_active) == QLA8044_FUN7_ACTIVE_INDEX)) {
  ha->flags.nic_core_reset_owner = 0;
  qla8044_device_bootstrap(vha);
 }
}

static void
qla8044_set_drv_active(struct scsi_qla_host *vha)
{
 uint32_t drv_active;
 struct qla_hw_data *ha = vha->hw;

 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);

 /* For ISP8044, drv_active register has 1 bit per function,
 * shift 1 by func_num to set a bit for the function.*/

 drv_active |= (1 << ha->portnum);

 ql_log(ql_log_info, vha, 0xb0c8,
     "%s(%ld): drv_active: 0x%08x\n",
     __func__, vha->host_no, drv_active);
 qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active);
}

static int
qla8044_check_drv_active(struct scsi_qla_host *vha)
{
 uint32_t drv_active;
 struct qla_hw_data *ha = vha->hw;

 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);
 if (drv_active & (1 << ha->portnum))
  return QLA_SUCCESS;
 else
  return QLA_TEST_FAILED;
}

static void
qla8044_clear_idc_dontreset(struct scsi_qla_host *vha)
{
 uint32_t idc_ctrl;
 struct qla_hw_data *ha = vha->hw;

 idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL);
 idc_ctrl &= ~DONTRESET_BIT0;
 ql_log(ql_log_info, vha, 0xb0c9,
     "%s: idc_ctrl = %d\n", __func__,
     idc_ctrl);
 qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl);
}

static int
qla8044_set_idc_ver(struct scsi_qla_host *vha)
{
 int idc_ver;
 uint32_t drv_active;
 int rval = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);
 if (drv_active == (1 << ha->portnum)) {
  idc_ver = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_IDC_VERSION_INDEX);
  idc_ver &= (~0xFF);
  idc_ver |= QLA8044_IDC_VER_MAJ_VALUE;
  qla8044_wr_direct(vha, QLA8044_CRB_DRV_IDC_VERSION_INDEX,
      idc_ver);
  ql_log(ql_log_info, vha, 0xb0ca,
      "%s: IDC version updated to %d\n",
      __func__, idc_ver);
 } else {
  idc_ver = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_IDC_VERSION_INDEX);
  idc_ver &= 0xFF;
  if (QLA8044_IDC_VER_MAJ_VALUE != idc_ver) {
   ql_log(ql_log_info, vha, 0xb0cb,
       "%s: qla4xxx driver IDC version %d "
       "is not compatible with IDC version %d "
       "of other drivers!\n",
       __func__, QLA8044_IDC_VER_MAJ_VALUE,
       idc_ver);
   rval = QLA_FUNCTION_FAILED;
   goto exit_set_idc_ver;
  }
 }

 /* Update IDC_MINOR_VERSION */
 idc_ver = qla8044_rd_reg(ha, QLA8044_CRB_IDC_VER_MINOR);
 idc_ver &= ~(0x03 << (ha->portnum * 2));
 idc_ver |= (QLA8044_IDC_VER_MIN_VALUE << (ha->portnum * 2));
 qla8044_wr_reg(ha, QLA8044_CRB_IDC_VER_MINOR, idc_ver);

exit_set_idc_ver:
 return rval;
}

static int
qla8044_update_idc_reg(struct scsi_qla_host *vha)
{
 uint32_t drv_active;
 int rval = QLA_SUCCESS;
 struct qla_hw_data *ha = vha->hw;

 if (vha->flags.init_done)
  goto exit_update_idc_reg;

 qla8044_idc_lock(ha);
 qla8044_set_drv_active(vha);

 drv_active = qla8044_rd_direct(vha,
     QLA8044_CRB_DRV_ACTIVE_INDEX);

 /* If we are the first driver to load and
 * ql2xdontresethba is not set, clear IDC_CTRL BIT0. */

 if ((drv_active == (1 << ha->portnum)) && !ql2xdontresethba)
  qla8044_clear_idc_dontreset(vha);

 rval = qla8044_set_idc_ver(vha);
 if (rval == QLA_FUNCTION_FAILED)
  qla8044_clear_drv_active(ha);
 qla8044_idc_unlock(ha);

exit_update_idc_reg:
 return rval;
}

/**
 * qla8044_need_qsnt_handler - Code to start qsnt
 * @vha: pointer to adapter structure
 */

static void
qla8044_need_qsnt_handler(struct scsi_qla_host *vha)
{
 unsigned long qsnt_timeout;
 uint32_t drv_state, drv_active, dev_state;
 struct qla_hw_data *ha = vha->hw;

 if (vha->flags.online)
  qla2x00_quiesce_io(vha);
 else
  return;

 qla8044_set_qsnt_ready(vha);

 /* Wait for 30 secs for all functions to ack qsnt mode */
 qsnt_timeout = jiffies + (QSNT_ACK_TOV * HZ);
 drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX);
 drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX);

 /* Shift drv_active by 1 to match drv_state. As quiescent ready bit
   position is at bit 1 and drv active is at bit 0 */

 drv_active = drv_active << 1;

 while (drv_state != drv_active) {
  if (time_after_eq(jiffies, qsnt_timeout)) {
   /* Other functions did not ack, changing state to
 * DEV_READY
 */

   clear_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
   qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX,
         QLA8XXX_DEV_READY);
   qla8044_clear_qsnt_ready(vha);
   ql_log(ql_log_info, vha, 0xb0cc,
       "Timeout waiting for quiescent ack!!!\n");
   return;
  }
  qla8044_idc_unlock(ha);
  msleep(1000);
  qla8044_idc_lock(ha);

  drv_state = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_STATE_INDEX);
  drv_active = qla8044_rd_direct(vha,
      QLA8044_CRB_DRV_ACTIVE_INDEX);
  drv_active = drv_active << 1;
 }

 /* All functions have Acked. Set quiescent state */
 dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);

 if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) {
  qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX,
      QLA8XXX_DEV_QUIESCENT);
  ql_log(ql_log_info, vha, 0xb0cd,
      "%s: HW State: QUIESCENT\n", __func__);
 }
}

/*
 * qla8044_device_state_handler - Adapter state machine
 * @ha: pointer to host adapter structure.
 *
 * Note: IDC lock must be UNLOCKED upon entry
 **/

int
qla8044_device_state_handler(struct scsi_qla_host *vha)
{
 uint32_t dev_state;
 int rval = QLA_SUCCESS;
 unsigned long dev_init_timeout;
 struct qla_hw_data *ha = vha->hw;

 rval = qla8044_update_idc_reg(vha);
 if (rval == QLA_FUNCTION_FAILED)
  goto exit_error;

 dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);
 ql_dbg(ql_dbg_p3p, vha, 0xb0ce,
     "Device state is 0x%x = %s\n",
     dev_state, qdev_state(dev_state));

 /* wait for 30 seconds for device to go ready */
 dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ);

 qla8044_idc_lock(ha);

 while (1) {
  if (time_after_eq(jiffies, dev_init_timeout)) {
   if (qla8044_check_drv_active(vha) == QLA_SUCCESS) {
    ql_log(ql_log_warn, vha, 0xb0cf,
        "%s: Device Init Failed 0x%x = %s\n",
        QLA2XXX_DRIVER_NAME, dev_state,
        qdev_state(dev_state));
    qla8044_wr_direct(vha,
        QLA8044_CRB_DEV_STATE_INDEX,
        QLA8XXX_DEV_FAILED);
   }
  }

  dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);
  ql_log(ql_log_info, vha, 0xb0d0,
      "Device state is 0x%x = %s\n",
      dev_state, qdev_state(dev_state));

  /* NOTE: Make sure idc unlocked upon exit of switch statement */
  switch (dev_state) {
  case QLA8XXX_DEV_READY:
   ha->flags.nic_core_reset_owner = 0;
   goto exit;
  case QLA8XXX_DEV_COLD:
   rval = qla8044_device_bootstrap(vha);
   break;
  case QLA8XXX_DEV_INITIALIZING:
   qla8044_idc_unlock(ha);
   msleep(1000);
   qla8044_idc_lock(ha);
   break;
  case QLA8XXX_DEV_NEED_RESET:
   /* For ISP8044, if NEED_RESET is set by any driver,
 * it should be honored, irrespective of IDC_CTRL
 * DONTRESET_BIT0 */

   qla8044_need_reset_handler(vha);
   break;
  case QLA8XXX_DEV_NEED_QUIESCENT:
   /* idc locked/unlocked in handler */
   qla8044_need_qsnt_handler(vha);

   /* Reset the init timeout after qsnt handler */
   dev_init_timeout = jiffies +
       (ha->fcoe_reset_timeout * HZ);
   break;
  case QLA8XXX_DEV_QUIESCENT:
   ql_log(ql_log_info, vha, 0xb0d1,
       "HW State: QUIESCENT\n");

   qla8044_idc_unlock(ha);
   msleep(1000);
   qla8044_idc_lock(ha);

   /* Reset the init timeout after qsnt handler */
   dev_init_timeout = jiffies +
       (ha->fcoe_reset_timeout * HZ);
   break;
  case QLA8XXX_DEV_FAILED:
   ha->flags.nic_core_reset_owner = 0;
   qla8044_idc_unlock(ha);
   qla8xxx_dev_failed_handler(vha);
   rval = QLA_FUNCTION_FAILED;
   qla8044_idc_lock(ha);
   goto exit;
  default:
   qla8044_idc_unlock(ha);
   qla8xxx_dev_failed_handler(vha);
   rval = QLA_FUNCTION_FAILED;
   qla8044_idc_lock(ha);
   goto exit;
  }
 }
exit:
 qla8044_idc_unlock(ha);

exit_error:
 return rval;
}

/**
 * qla8044_check_temp - Check the ISP82XX temperature.
 * @vha: adapter block pointer.
 *
 * Note: The caller should not hold the idc lock.
 */

static int
qla8044_check_temp(struct scsi_qla_host *vha)
{
 uint32_t temp, temp_state, temp_val;
 int status = QLA_SUCCESS;

 temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX);
 temp_state = qla82xx_get_temp_state(temp);
 temp_val = qla82xx_get_temp_val(temp);

 if (temp_state == QLA82XX_TEMP_PANIC) {
  ql_log(ql_log_warn, vha, 0xb0d2,
      "Device temperature %d degrees C"
      " exceeds maximum allowed. Hardware has been shut"
      " down\n", temp_val);
  status = QLA_FUNCTION_FAILED;
  return status;
 } else if (temp_state == QLA82XX_TEMP_WARN) {
  ql_log(ql_log_warn, vha, 0xb0d3,
      "Device temperature %d"
      " degrees C exceeds operating range."
      " Immediate action needed.\n", temp_val);
 }
 return 0;
}

int qla8044_read_temperature(scsi_qla_host_t *vha)
{
 uint32_t temp;

 temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX);
 return qla82xx_get_temp_val(temp);
}

/**
 * qla8044_check_fw_alive  - Check firmware health
 * @vha: Pointer to host adapter structure.
 *
 * Context: Interrupt
 */

int
qla8044_check_fw_alive(struct scsi_qla_host *vha)
{
 uint32_t fw_heartbeat_counter;
 uint32_t halt_status1, halt_status2;
 int status = QLA_SUCCESS;

 fw_heartbeat_counter = qla8044_rd_direct(vha,
     QLA8044_PEG_ALIVE_COUNTER_INDEX);

 /* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */
 if (fw_heartbeat_counter == 0xffffffff) {
  ql_dbg(ql_dbg_p3p, vha, 0xb0d4,
      "scsi%ld: %s: Device in frozen "
      "state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n",
      vha->host_no, __func__);
  return status;
 }

 if (vha->fw_heartbeat_counter == fw_heartbeat_counter) {
  vha->seconds_since_last_heartbeat++;
  /* FW not alive after 2 seconds */
  if (vha->seconds_since_last_heartbeat == 2) {
   vha->seconds_since_last_heartbeat = 0;
   halt_status1 = qla8044_rd_direct(vha,
       QLA8044_PEG_HALT_STATUS1_INDEX);
   halt_status2 = qla8044_rd_direct(vha,
       QLA8044_PEG_HALT_STATUS2_INDEX);

   ql_log(ql_log_info, vha, 0xb0d5,
       "scsi(%ld): %s, ISP8044 "
       "Dumping hw/fw registers:\n"
       " PEG_HALT_STATUS1: 0x%x, "
       "PEG_HALT_STATUS2: 0x%x,\n",
       vha->host_no, __func__, halt_status1,
       halt_status2);
   status = QLA_FUNCTION_FAILED;
  }
 } else
  vha->seconds_since_last_heartbeat = 0;

 vha->fw_heartbeat_counter = fw_heartbeat_counter;
 return status;
}

void
qla8044_watchdog(struct scsi_qla_host *vha)
{
 uint32_t dev_state, halt_status;
 int halt_status_unrecoverable = 0;
 struct qla_hw_data *ha = vha->hw;

 /* don't poll if reset is going on or FW hang in quiescent state */
 if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
     test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))) {
  dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);

  if (qla8044_check_fw_alive(vha)) {
   ha->flags.isp82xx_fw_hung = 1;
   ql_log(ql_log_warn, vha, 0xb10a,
       "Firmware hung.\n");
   qla82xx_clear_pending_mbx(vha);
  }

  if (qla8044_check_temp(vha)) {
   set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags);
   ha->flags.isp82xx_fw_hung = 1;
   qla2xxx_wake_dpc(vha);
  } else if (dev_state == QLA8XXX_DEV_NEED_RESET &&
      !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
   ql_log(ql_log_info, vha, 0xb0d6,
       "%s: HW State: NEED RESET!\n",
       __func__);
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
   qla2xxx_wake_dpc(vha);
  } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT &&
      !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
   ql_log(ql_log_info, vha, 0xb0d7,
       "%s: HW State: NEED QUIES detected!\n",
       __func__);
   set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
   qla2xxx_wake_dpc(vha);
  } else  {
   /* Check firmware health */
   if (ha->flags.isp82xx_fw_hung) {
    halt_status = qla8044_rd_direct(vha,
     QLA8044_PEG_HALT_STATUS1_INDEX);
    if (halt_status &
        QLA8044_HALT_STATUS_FW_RESET) {
     ql_log(ql_log_fatal, vha,
         0xb0d8, "%s: Firmware "
         "error detected device "
         "is being reset\n",
         __func__);
    } else if (halt_status &
         QLA8044_HALT_STATUS_UNRECOVERABLE) {
      halt_status_unrecoverable = 1;
    }

    /* Since we cannot change dev_state in interrupt
 * context, set appropriate DPC flag then wakeup
 *  DPC */

    if (halt_status_unrecoverable) {
     set_bit(ISP_UNRECOVERABLE,
         &vha->dpc_flags);
    } else {
     if (dev_state ==
         QLA8XXX_DEV_QUIESCENT) {
      set_bit(FCOE_CTX_RESET_NEEDED,
          &vha->dpc_flags);
      ql_log(ql_log_info, vha, 0xb0d9,
          "%s: FW CONTEXT Reset "
          "needed!\n", __func__);
     } else {
      ql_log(ql_log_info, vha,
          0xb0da, "%s: "
          "detect abort needed\n",
          __func__);
      set_bit(ISP_ABORT_NEEDED,
          &vha->dpc_flags);
     }
    }
    qla2xxx_wake_dpc(vha);
   }
  }

 }
}

static int
qla8044_minidump_process_control(struct scsi_qla_host *vha,
     struct qla8044_minidump_entry_hdr *entry_hdr)
{
 struct qla8044_minidump_entry_crb *crb_entry;
 uint32_t read_value, opcode, poll_time, addr, index;
 uint32_t crb_addr, rval = QLA_SUCCESS;
 unsigned long wtime;
 struct qla8044_minidump_template_hdr *tmplt_hdr;
 int i;
 struct qla_hw_data *ha = vha->hw;

 ql_dbg(ql_dbg_p3p, vha, 0xb0dd, "Entering fn: %s\n", __func__);
 tmplt_hdr = (struct qla8044_minidump_template_hdr *)
  ha->md_tmplt_hdr;
 crb_entry = (struct qla8044_minidump_entry_crb *)entry_hdr;

 crb_addr = crb_entry->addr;
 for (i = 0; i < crb_entry->op_count; i++) {
  opcode = crb_entry->crb_ctrl.opcode;

  if (opcode & QLA82XX_DBG_OPCODE_WR) {
   qla8044_wr_reg_indirect(vha, crb_addr,
       crb_entry->value_1);
  }

  if (opcode & QLA82XX_DBG_OPCODE_RW) {
   qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
   qla8044_wr_reg_indirect(vha, crb_addr, read_value);
  }

  if (opcode & QLA82XX_DBG_OPCODE_AND) {
   qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
   read_value &= crb_entry->value_2;
   if (opcode & QLA82XX_DBG_OPCODE_OR) {
    read_value |= crb_entry->value_3;
    opcode &= ~QLA82XX_DBG_OPCODE_OR;
   }
   qla8044_wr_reg_indirect(vha, crb_addr, read_value);
  }
  if (opcode & QLA82XX_DBG_OPCODE_OR) {
   qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
   read_value |= crb_entry->value_3;
   qla8044_wr_reg_indirect(vha, crb_addr, read_value);
  }
  if (opcode & QLA82XX_DBG_OPCODE_POLL) {
   poll_time = crb_entry->crb_strd.poll_timeout;
   wtime = jiffies + poll_time;
   qla8044_rd_reg_indirect(vha, crb_addr, &read_value);

   do {
    if ((read_value & crb_entry->value_2) ==
        crb_entry->value_1) {
     break;
    } else if (time_after_eq(jiffies, wtime)) {
     /* capturing dump failed */
     rval = QLA_FUNCTION_FAILED;
     break;
    } else {
     qla8044_rd_reg_indirect(vha,
         crb_addr, &read_value);
    }
   } while (1);
  }

  if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
   if (crb_entry->crb_strd.state_index_a) {
    index = crb_entry->crb_strd.state_index_a;
    addr = tmplt_hdr->saved_state_array[index];
   } else {
    addr = crb_addr;
   }

   qla8044_rd_reg_indirect(vha, addr, &read_value);
   index = crb_entry->crb_ctrl.state_index_v;
   tmplt_hdr->saved_state_array[index] = read_value;
  }

  if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
   if (crb_entry->crb_strd.state_index_a) {
    index = crb_entry->crb_strd.state_index_a;
    addr = tmplt_hdr->saved_state_array[index];
   } else {
    addr = crb_addr;
   }

   if (crb_entry->crb_ctrl.state_index_v) {
    index = crb_entry->crb_ctrl.state_index_v;
    read_value =
        tmplt_hdr->saved_state_array[index];
   } else {
    read_value = crb_entry->value_1;
   }

   qla8044_wr_reg_indirect(vha, addr, read_value);
  }

  if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
   index = crb_entry->crb_ctrl.state_index_v;
   read_value = tmplt_hdr->saved_state_array[index];
   read_value <<= crb_entry->crb_ctrl.shl;
   read_value >>= crb_entry->crb_ctrl.shr;
   if (crb_entry->value_2)
    read_value &= crb_entry->value_2;
   read_value |= crb_entry->value_3;
   read_value += crb_entry->value_1;
   tmplt_hdr->saved_state_array[index] = read_value;
  }
  crb_addr += crb_entry->crb_strd.addr_stride;
 }
 return rval;
}

static void
qla8044_minidump_process_rdcrb(struct scsi_qla_host *vha,
 struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
{
 uint32_t r_addr, r_stride, loop_cnt, i, r_value;
 struct qla8044_minidump_entry_crb *crb_hdr;
 uint32_t *data_ptr = *d_ptr;

 ql_dbg(ql_dbg_p3p, vha, 0xb0de, "Entering fn: %s\n", __func__);
 crb_hdr = (struct qla8044_minidump_entry_crb *)entry_hdr;
 r_addr = crb_hdr->addr;
 r_stride = crb_hdr->crb_strd.addr_stride;
 loop_cnt = crb_hdr->op_count;

 for (i = 0; i < loop_cnt; i++) {
  qla8044_rd_reg_indirect(vha, r_addr, &r_value);
  *data_ptr++ = r_addr;
  *data_ptr++ = r_value;
  r_addr += r_stride;
 }
 *d_ptr = data_ptr;
}

static int
qla8044_minidump_process_rdmem(struct scsi_qla_host *vha,
 struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
{
 uint32_t r_addr, r_value, r_data;
 uint32_t i, j, loop_cnt;
 struct qla8044_minidump_entry_rdmem *m_hdr;
 unsigned long flags;
 uint32_t *data_ptr = *d_ptr;
 struct qla_hw_data *ha = vha->hw;

 ql_dbg(ql_dbg_p3p, vha, 0xb0df, "Entering fn: %s\n", __func__);
 m_hdr = (struct qla8044_minidump_entry_rdmem *)entry_hdr;
 r_addr = m_hdr->read_addr;
 loop_cnt = m_hdr->read_data_size/16;

 ql_dbg(ql_dbg_p3p, vha, 0xb0f0,
     "[%s]: Read addr: 0x%x, read_data_size: 0x%x\n",
     __func__, r_addr, m_hdr->read_data_size);

 if (r_addr & 0xf) {
  ql_dbg(ql_dbg_p3p, vha, 0xb0f1,
      "[%s]: Read addr 0x%x not 16 bytes aligned\n",
      __func__, r_addr);
  return QLA_FUNCTION_FAILED;
 }

 if (m_hdr->read_data_size % 16) {
  ql_dbg(ql_dbg_p3p, vha, 0xb0f2,
      "[%s]: Read data[0x%x] not multiple of 16 bytes\n",
      __func__, m_hdr->read_data_size);
  return QLA_FUNCTION_FAILED;
 }

 ql_dbg(ql_dbg_p3p, vha, 0xb0f3,
     "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n",
     __func__, r_addr, m_hdr->read_data_size, loop_cnt);

 write_lock_irqsave(&ha->hw_lock, flags);
 for (i = 0; i < loop_cnt; i++) {
  qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_LO, r_addr);
  r_value = 0;
  qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, r_value);
  r_value = MIU_TA_CTL_ENABLE;
  qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value);
  r_value = MIU_TA_CTL_START_ENABLE;
  qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value);

  for (j = 0; j < MAX_CTL_CHECK; j++) {
   qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL,
       &r_value);
   if ((r_value & MIU_TA_CTL_BUSY) == 0)
    break;
  }

  if (j >= MAX_CTL_CHECK) {
   write_unlock_irqrestore(&ha->hw_lock, flags);
   return QLA_SUCCESS;
  }

  for (j = 0; j < 4; j++) {
   qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_RDDATA[j],
       &r_data);
   *data_ptr++ = r_data;
  }

  r_addr += 16;
 }
 write_unlock_irqrestore(&ha->hw_lock, flags);

 ql_dbg(ql_dbg_p3p, vha, 0xb0f4,
     "Leaving fn: %s datacount: 0x%x\n",
      __func__, (loop_cnt * 16));

 *d_ptr = data_ptr;
 return QLA_SUCCESS;
}

/* ISP83xx flash read for _RDROM _BOARD */
static uint32_t
qla8044_minidump_process_rdrom(struct scsi_qla_host *vha,
 struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr)
{
 uint32_t fl_addr, u32_count, rval;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=80 G=88

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.