|
@@ -2149,7 +2149,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
scsi_qla_host_t *base_vha = NULL;
|
|
|
struct qla_hw_data *ha;
|
|
|
char pci_info[30];
|
|
|
- char fw_str[30];
|
|
|
+ char fw_str[30], wq_name[30];
|
|
|
struct scsi_host_template *sht;
|
|
|
int bars, mem_only = 0;
|
|
|
uint16_t req_length = 0, rsp_length = 0;
|
|
@@ -2319,6 +2319,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
|
|
|
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
|
|
|
} else if (IS_QLA83XX(ha)) {
|
|
|
+ ha->portnum = PCI_FUNC(ha->pdev->devfn);
|
|
|
ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
|
|
|
ha->mbx_count = MAILBOX_REGISTER_COUNT;
|
|
|
req_length = REQUEST_ENTRY_CNT_24XX;
|
|
@@ -2403,6 +2404,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
|
|
|
base_vha->vp_idx;
|
|
|
|
|
|
+ if (IS_QLA8031(ha)) {
|
|
|
+ sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no);
|
|
|
+ ha->dpc_lp_wq = create_singlethread_workqueue(wq_name);
|
|
|
+ INIT_WORK(&ha->idc_aen, qla83xx_service_idc_aen);
|
|
|
+
|
|
|
+ sprintf(wq_name, "qla2xxx_%lu_dpc_hp_wq", base_vha->host_no);
|
|
|
+ ha->dpc_hp_wq = create_singlethread_workqueue(wq_name);
|
|
|
+ INIT_WORK(&ha->nic_core_reset, qla83xx_nic_core_reset_work);
|
|
|
+ INIT_WORK(&ha->idc_state_handler,
|
|
|
+ qla83xx_idc_state_handler_work);
|
|
|
+ INIT_WORK(&ha->nic_core_unrecoverable,
|
|
|
+ qla83xx_nic_core_unrecoverable_work);
|
|
|
+ }
|
|
|
+
|
|
|
/* Set the SG table size based on ISP type */
|
|
|
if (!IS_FWI2_CAPABLE(ha)) {
|
|
|
if (IS_QLA2100(ha))
|
|
@@ -2500,7 +2515,7 @@ que_init:
|
|
|
if (IS_QLA82XX(ha)) {
|
|
|
qla82xx_idc_lock(ha);
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_FAILED);
|
|
|
+ QLA8XXX_DEV_FAILED);
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
ql_log(ql_log_fatal, base_vha, 0x00d7,
|
|
|
"HW State: FAILED.\n");
|
|
@@ -2751,6 +2766,14 @@ qla2x00_remove_one(struct pci_dev *pdev)
|
|
|
}
|
|
|
mutex_unlock(&ha->vport_lock);
|
|
|
|
|
|
+ if (IS_QLA8031(ha)) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb07e,
|
|
|
+ "Clearing fcoe driver presence.\n");
|
|
|
+ if (qla83xx_clear_drv_presence(base_vha) != QLA_SUCCESS)
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb079,
|
|
|
+ "Error while clearing DRV-Presence.\n");
|
|
|
+ }
|
|
|
+
|
|
|
set_bit(UNLOADING, &base_vha->dpc_flags);
|
|
|
|
|
|
qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
|
|
@@ -2772,6 +2795,21 @@ qla2x00_remove_one(struct pci_dev *pdev)
|
|
|
ha->wq = NULL;
|
|
|
}
|
|
|
|
|
|
+ /* Cancel all work and destroy DPC workqueues */
|
|
|
+ if (ha->dpc_lp_wq) {
|
|
|
+ cancel_work_sync(&ha->idc_aen);
|
|
|
+ destroy_workqueue(ha->dpc_lp_wq);
|
|
|
+ ha->dpc_lp_wq = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ha->dpc_hp_wq) {
|
|
|
+ cancel_work_sync(&ha->nic_core_reset);
|
|
|
+ cancel_work_sync(&ha->idc_state_handler);
|
|
|
+ cancel_work_sync(&ha->nic_core_unrecoverable);
|
|
|
+ destroy_workqueue(ha->dpc_hp_wq);
|
|
|
+ ha->dpc_hp_wq = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
/* Kill the kernel thread for this host */
|
|
|
if (ha->dpc_thread) {
|
|
|
struct task_struct *t = ha->dpc_thread;
|
|
@@ -2838,7 +2876,6 @@ qla2x00_free_device(scsi_qla_host_t *vha)
|
|
|
qla2x00_stop_dpc_thread(vha);
|
|
|
|
|
|
qla25xx_delete_queues(vha);
|
|
|
-
|
|
|
if (ha->flags.fce_enabled)
|
|
|
qla2x00_disable_fce_trace(vha, NULL, NULL);
|
|
|
|
|
@@ -3709,6 +3746,637 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* Schedule work on any of the dpc-workqueues */
|
|
|
+void
|
|
|
+qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = base_vha->hw;
|
|
|
+
|
|
|
+ switch (work_code) {
|
|
|
+ case MBA_IDC_AEN: /* 0x8200 */
|
|
|
+ if (ha->dpc_lp_wq)
|
|
|
+ queue_work(ha->dpc_lp_wq, &ha->idc_aen);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case QLA83XX_NIC_CORE_RESET: /* 0x1 */
|
|
|
+ if (!ha->flags.nic_core_reset_hdlr_active) {
|
|
|
+ if (ha->dpc_hp_wq)
|
|
|
+ queue_work(ha->dpc_hp_wq, &ha->nic_core_reset);
|
|
|
+ } else
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb05e,
|
|
|
+ "NIC Core reset is already active. Skip "
|
|
|
+ "scheduling it again.\n");
|
|
|
+ break;
|
|
|
+ case QLA83XX_IDC_STATE_HANDLER: /* 0x2 */
|
|
|
+ if (ha->dpc_hp_wq)
|
|
|
+ queue_work(ha->dpc_hp_wq, &ha->idc_state_handler);
|
|
|
+ break;
|
|
|
+ case QLA83XX_NIC_CORE_UNRECOVERABLE: /* 0x3 */
|
|
|
+ if (ha->dpc_hp_wq)
|
|
|
+ queue_work(ha->dpc_hp_wq, &ha->nic_core_unrecoverable);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ql_log(ql_log_warn, base_vha, 0xb05f,
|
|
|
+ "Unknow work-code=0x%x.\n", work_code);
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/* Work: Perform NIC Core Unrecoverable state handling */
|
|
|
+void
|
|
|
+qla83xx_nic_core_unrecoverable_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha =
|
|
|
+ container_of(work, struct qla_hw_data, nic_core_reset);
|
|
|
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
+ uint32_t dev_state = 0;
|
|
|
+
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
+ qla83xx_reset_ownership(base_vha);
|
|
|
+ if (ha->flags.nic_core_reset_owner) {
|
|
|
+ ha->flags.nic_core_reset_owner = 0;
|
|
|
+ qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
+ QLA8XXX_DEV_FAILED);
|
|
|
+ ql_log(ql_log_info, base_vha, 0xb060, "HW State: FAILED.\n");
|
|
|
+ qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER);
|
|
|
+ }
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/* Work: Execute IDC state handler */
|
|
|
+void
|
|
|
+qla83xx_idc_state_handler_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha =
|
|
|
+ container_of(work, struct qla_hw_data, nic_core_reset);
|
|
|
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
+ uint32_t dev_state = 0;
|
|
|
+
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
+ if (dev_state == QLA8XXX_DEV_FAILED ||
|
|
|
+ dev_state == QLA8XXX_DEV_NEED_QUIESCENT)
|
|
|
+ qla83xx_idc_state_handler(base_vha);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ unsigned long heart_beat_wait = jiffies + (1 * HZ);
|
|
|
+ uint32_t heart_beat_counter1, heart_beat_counter2;
|
|
|
+
|
|
|
+ do {
|
|
|
+ if (time_after(jiffies, heart_beat_wait)) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb07c,
|
|
|
+ "Nic Core f/w is not alive.\n");
|
|
|
+ rval = QLA_FUNCTION_FAILED;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT,
|
|
|
+ &heart_beat_counter1);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(100);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT,
|
|
|
+ &heart_beat_counter2);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ } while (heart_beat_counter1 == heart_beat_counter2);
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+/* Work: Perform NIC Core Reset handling */
|
|
|
+void
|
|
|
+qla83xx_nic_core_reset_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha =
|
|
|
+ container_of(work, struct qla_hw_data, nic_core_reset);
|
|
|
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
+ uint32_t dev_state = 0;
|
|
|
+
|
|
|
+ if (!ha->flags.nic_core_reset_hdlr_active) {
|
|
|
+ if (qla83xx_check_nic_core_fw_alive(base_vha) == QLA_SUCCESS) {
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
+ &dev_state);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ if (dev_state != QLA8XXX_DEV_NEED_RESET) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb07a,
|
|
|
+ "Nic Core f/w is alive.\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ha->flags.nic_core_reset_hdlr_active = 1;
|
|
|
+ if (qla83xx_nic_core_reset(base_vha)) {
|
|
|
+ /* NIC Core reset failed. */
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb061,
|
|
|
+ "NIC Core reset failed.\n");
|
|
|
+ }
|
|
|
+ ha->flags.nic_core_reset_hdlr_active = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Work: Handle 8200 IDC aens */
|
|
|
+void
|
|
|
+qla83xx_service_idc_aen(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha =
|
|
|
+ container_of(work, struct qla_hw_data, idc_aen);
|
|
|
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
|
|
|
+ uint32_t dev_state, idc_control;
|
|
|
+
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_CONTROL, &idc_control);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ if (dev_state == QLA8XXX_DEV_NEED_RESET) {
|
|
|
+ if (idc_control & QLA83XX_IDC_GRACEFUL_RESET) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb062,
|
|
|
+ "Application requested NIC Core Reset.\n");
|
|
|
+ qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET);
|
|
|
+ } else if (qla83xx_check_nic_core_fw_alive(base_vha) ==
|
|
|
+ QLA_SUCCESS) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb07b,
|
|
|
+ "Other protocol driver requested NIC Core Reset.\n");
|
|
|
+ qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET);
|
|
|
+ }
|
|
|
+ } else if (dev_state == QLA8XXX_DEV_FAILED ||
|
|
|
+ dev_state == QLA8XXX_DEV_NEED_QUIESCENT) {
|
|
|
+ qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+qla83xx_wait_logic(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Yield CPU */
|
|
|
+ if (!in_interrupt()) {
|
|
|
+ /*
|
|
|
+ * Wait about 200ms before retrying again.
|
|
|
+ * This controls the number of retries for single
|
|
|
+ * lock operation.
|
|
|
+ */
|
|
|
+ msleep(100);
|
|
|
+ schedule();
|
|
|
+ } else {
|
|
|
+ for (i = 0; i < 20; i++)
|
|
|
+ cpu_relax(); /* This a nop instr on i386 */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha)
|
|
|
+{
|
|
|
+ int rval;
|
|
|
+ uint32_t data;
|
|
|
+ uint32_t idc_lck_rcvry_stage_mask = 0x3;
|
|
|
+ uint32_t idc_lck_rcvry_owner_mask = 0x3c;
|
|
|
+ struct qla_hw_data *ha = base_vha->hw;
|
|
|
+
|
|
|
+ rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, &data);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+
|
|
|
+ if ((data & idc_lck_rcvry_stage_mask) > 0) {
|
|
|
+ return QLA_SUCCESS;
|
|
|
+ } else {
|
|
|
+ data = (IDC_LOCK_RECOVERY_STAGE1) | (ha->portnum << 2);
|
|
|
+ rval = qla83xx_wr_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY,
|
|
|
+ data);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+
|
|
|
+ msleep(200);
|
|
|
+
|
|
|
+ rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY,
|
|
|
+ &data);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+
|
|
|
+ if (((data & idc_lck_rcvry_owner_mask) >> 2) == ha->portnum) {
|
|
|
+ data &= (IDC_LOCK_RECOVERY_STAGE2 |
|
|
|
+ ~(idc_lck_rcvry_stage_mask));
|
|
|
+ rval = qla83xx_wr_reg(base_vha,
|
|
|
+ QLA83XX_IDC_LOCK_RECOVERY, data);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+
|
|
|
+ /* Forcefully perform IDC UnLock */
|
|
|
+ rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK,
|
|
|
+ &data);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+ /* Clear lock-id by setting 0xff */
|
|
|
+ rval = qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID,
|
|
|
+ 0xff);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+ /* Clear lock-recovery by setting 0x0 */
|
|
|
+ rval = qla83xx_wr_reg(base_vha,
|
|
|
+ QLA83XX_IDC_LOCK_RECOVERY, 0x0);
|
|
|
+ if (rval)
|
|
|
+ return rval;
|
|
|
+ } else
|
|
|
+ return QLA_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ uint32_t o_drv_lockid, n_drv_lockid;
|
|
|
+ unsigned long lock_recovery_timeout;
|
|
|
+
|
|
|
+ lock_recovery_timeout = jiffies + QLA83XX_MAX_LOCK_RECOVERY_WAIT;
|
|
|
+retry_lockid:
|
|
|
+ rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &o_drv_lockid);
|
|
|
+ if (rval)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
+ /* MAX wait time before forcing IDC Lock recovery = 2 secs */
|
|
|
+ if (time_after_eq(jiffies, lock_recovery_timeout)) {
|
|
|
+ if (qla83xx_force_lock_recovery(base_vha) == QLA_SUCCESS)
|
|
|
+ return QLA_SUCCESS;
|
|
|
+ else
|
|
|
+ return QLA_FUNCTION_FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &n_drv_lockid);
|
|
|
+ if (rval)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
+ if (o_drv_lockid == n_drv_lockid) {
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ goto retry_lockid;
|
|
|
+ } else
|
|
|
+ return QLA_SUCCESS;
|
|
|
+
|
|
|
+exit:
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
|
|
|
+{
|
|
|
+ uint16_t options = (requester_id << 15) | BIT_6;
|
|
|
+ uint32_t data;
|
|
|
+ struct qla_hw_data *ha = base_vha->hw;
|
|
|
+
|
|
|
+ /* IDC-lock implementation using driver-lock/lock-id remote registers */
|
|
|
+retry_lock:
|
|
|
+ if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data)
|
|
|
+ == QLA_SUCCESS) {
|
|
|
+ if (data) {
|
|
|
+ /* Setting lock-id to our function-number */
|
|
|
+ qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID,
|
|
|
+ ha->portnum);
|
|
|
+ } else {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb063,
|
|
|
+ "Failed to acquire IDC lock. retrying...\n");
|
|
|
+
|
|
|
+ /* Retry/Perform IDC-Lock recovery */
|
|
|
+ if (qla83xx_idc_lock_recovery(base_vha)
|
|
|
+ == QLA_SUCCESS) {
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ goto retry_lock;
|
|
|
+ } else
|
|
|
+ ql_log(ql_log_warn, base_vha, 0xb075,
|
|
|
+ "IDC Lock recovery FAILED.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* XXX: IDC-lock implementation using access-control mbx */
|
|
|
+retry_lock2:
|
|
|
+ if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) {
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb072,
|
|
|
+ "Failed to acquire IDC lock. retrying...\n");
|
|
|
+ /* Retry/Perform IDC-Lock recovery */
|
|
|
+ if (qla83xx_idc_lock_recovery(base_vha) == QLA_SUCCESS) {
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ goto retry_lock2;
|
|
|
+ } else
|
|
|
+ ql_log(ql_log_warn, base_vha, 0xb076,
|
|
|
+ "IDC Lock recovery FAILED.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id)
|
|
|
+{
|
|
|
+ uint16_t options = (requester_id << 15) | BIT_7, retry;
|
|
|
+ uint32_t data;
|
|
|
+ struct qla_hw_data *ha = base_vha->hw;
|
|
|
+
|
|
|
+ /* IDC-unlock implementation using driver-unlock/lock-id
|
|
|
+ * remote registers
|
|
|
+ */
|
|
|
+ retry = 0;
|
|
|
+retry_unlock:
|
|
|
+ if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &data)
|
|
|
+ == QLA_SUCCESS) {
|
|
|
+ if (data == ha->portnum) {
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, &data);
|
|
|
+ /* Clearing lock-id by setting 0xff */
|
|
|
+ qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, 0xff);
|
|
|
+ } else if (retry < 10) {
|
|
|
+ /* SV: XXX: IDC unlock retrying needed here? */
|
|
|
+
|
|
|
+ /* Retry for IDC-unlock */
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ retry++;
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb064,
|
|
|
+ "Failed to release IDC lock, retyring=%d\n", retry);
|
|
|
+ goto retry_unlock;
|
|
|
+ }
|
|
|
+ } else if (retry < 10) {
|
|
|
+ /* Retry for IDC-unlock */
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ retry++;
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb065,
|
|
|
+ "Failed to read drv-lockid, retyring=%d\n", retry);
|
|
|
+ goto retry_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* XXX: IDC-unlock implementation using access-control mbx */
|
|
|
+ retry = 0;
|
|
|
+retry_unlock2:
|
|
|
+ if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) {
|
|
|
+ if (retry < 10) {
|
|
|
+ /* Retry for IDC-unlock */
|
|
|
+ qla83xx_wait_logic();
|
|
|
+ retry++;
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb066,
|
|
|
+ "Failed to release IDC lock, retyring=%d\n", retry);
|
|
|
+ goto retry_unlock2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+__qla83xx_set_drv_presence(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t drv_presence;
|
|
|
+
|
|
|
+ rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
|
|
+ if (rval == QLA_SUCCESS) {
|
|
|
+ drv_presence |= (1 << ha->portnum);
|
|
|
+ rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
+ drv_presence);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_set_drv_presence(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+
|
|
|
+ qla83xx_idc_lock(vha, 0);
|
|
|
+ rval = __qla83xx_set_drv_presence(vha);
|
|
|
+ qla83xx_idc_unlock(vha, 0);
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+__qla83xx_clear_drv_presence(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t drv_presence;
|
|
|
+
|
|
|
+ rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
|
|
+ if (rval == QLA_SUCCESS) {
|
|
|
+ drv_presence &= ~(1 << ha->portnum);
|
|
|
+ rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
+ drv_presence);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_clear_drv_presence(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+
|
|
|
+ qla83xx_idc_lock(vha, 0);
|
|
|
+ rval = __qla83xx_clear_drv_presence(vha);
|
|
|
+ qla83xx_idc_unlock(vha, 0);
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+qla83xx_need_reset_handler(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t drv_ack, drv_presence;
|
|
|
+ unsigned long ack_timeout;
|
|
|
+
|
|
|
+ /* Wait for IDC ACK from all functions (DRV-ACK == DRV-PRESENCE) */
|
|
|
+ ack_timeout = jiffies + (ha->fcoe_reset_timeout * HZ);
|
|
|
+ while (1) {
|
|
|
+ qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack);
|
|
|
+ qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
|
|
|
+ if (drv_ack == drv_presence)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (time_after_eq(jiffies, ack_timeout)) {
|
|
|
+ ql_log(ql_log_warn, vha, 0xb067,
|
|
|
+ "RESET ACK TIMEOUT! drv_presence=0x%x "
|
|
|
+ "drv_ack=0x%x\n", drv_presence, drv_ack);
|
|
|
+ /*
|
|
|
+ * The function(s) which did not ack in time are forced
|
|
|
+ * to withdraw any further participation in the IDC
|
|
|
+ * reset.
|
|
|
+ */
|
|
|
+ if (drv_ack != drv_presence)
|
|
|
+ qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE,
|
|
|
+ drv_ack);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ qla83xx_idc_unlock(vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(vha, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_COLD);
|
|
|
+ ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n");
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+qla83xx_device_bootstrap(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ uint32_t idc_control;
|
|
|
+
|
|
|
+ qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_INITIALIZING);
|
|
|
+ ql_log(ql_log_info, vha, 0xb069, "HW State: INITIALIZING.\n");
|
|
|
+
|
|
|
+ /* Clearing IDC-Control Graceful-Reset Bit before resetting f/w */
|
|
|
+ __qla83xx_get_idc_control(vha, &idc_control);
|
|
|
+ idc_control &= ~QLA83XX_IDC_GRACEFUL_RESET;
|
|
|
+ __qla83xx_set_idc_control(vha, 0);
|
|
|
+
|
|
|
+ qla83xx_idc_unlock(vha, 0);
|
|
|
+ rval = qla83xx_restart_nic_firmware(vha);
|
|
|
+ qla83xx_idc_lock(vha, 0);
|
|
|
+
|
|
|
+ if (rval != QLA_SUCCESS) {
|
|
|
+ ql_log(ql_log_fatal, vha, 0xb06a,
|
|
|
+ "Failed to restart NIC f/w.\n");
|
|
|
+ qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_FAILED);
|
|
|
+ ql_log(ql_log_info, vha, 0xb06b, "HW State: FAILED.\n");
|
|
|
+ } else {
|
|
|
+ ql_dbg(ql_dbg_p3p, vha, 0xb06c,
|
|
|
+ "Success in restarting nic f/w.\n");
|
|
|
+ qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_READY);
|
|
|
+ ql_log(ql_log_info, vha, 0xb06d, "HW State: READY.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+/* Assumes idc_lock always held on entry */
|
|
|
+int
|
|
|
+qla83xx_idc_state_handler(scsi_qla_host_t *base_vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = base_vha->hw;
|
|
|
+ int rval = QLA_SUCCESS;
|
|
|
+ unsigned long dev_init_timeout;
|
|
|
+ uint32_t dev_state;
|
|
|
+
|
|
|
+ /* Wait for MAX-INIT-TIMEOUT for the device to go ready */
|
|
|
+ dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ);
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+
|
|
|
+ if (time_after_eq(jiffies, dev_init_timeout)) {
|
|
|
+ ql_log(ql_log_warn, base_vha, 0xb06e,
|
|
|
+ "Initialization TIMEOUT!\n");
|
|
|
+ /* Init timeout. Disable further NIC Core
|
|
|
+ * communication.
|
|
|
+ */
|
|
|
+ qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE,
|
|
|
+ QLA8XXX_DEV_FAILED);
|
|
|
+ ql_log(ql_log_info, base_vha, 0xb06f,
|
|
|
+ "HW State: FAILED.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state);
|
|
|
+ switch (dev_state) {
|
|
|
+ case QLA8XXX_DEV_READY:
|
|
|
+ if (ha->flags.nic_core_reset_owner)
|
|
|
+ qla83xx_idc_audit(base_vha,
|
|
|
+ IDC_AUDIT_COMPLETION);
|
|
|
+ ha->flags.nic_core_reset_owner = 0;
|
|
|
+ ql_dbg(ql_dbg_p3p, base_vha, 0xb070,
|
|
|
+ "Reset_owner reset by 0x%x.\n",
|
|
|
+ ha->portnum);
|
|
|
+ goto exit;
|
|
|
+ case QLA8XXX_DEV_COLD:
|
|
|
+ if (ha->flags.nic_core_reset_owner)
|
|
|
+ rval = qla83xx_device_bootstrap(base_vha);
|
|
|
+ else {
|
|
|
+ /* Wait for AEN to change device-state */
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case QLA8XXX_DEV_INITIALIZING:
|
|
|
+ /* Wait for AEN to change device-state */
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ break;
|
|
|
+ case QLA8XXX_DEV_NEED_RESET:
|
|
|
+ if (!ql2xdontresethba && ha->flags.nic_core_reset_owner)
|
|
|
+ qla83xx_need_reset_handler(base_vha);
|
|
|
+ else {
|
|
|
+ /* Wait for AEN to change device-state */
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ }
|
|
|
+ /* reset timeout value after need reset handler */
|
|
|
+ dev_init_timeout = jiffies +
|
|
|
+ (ha->fcoe_dev_init_timeout * HZ);
|
|
|
+ break;
|
|
|
+ case QLA8XXX_DEV_NEED_QUIESCENT:
|
|
|
+ /* XXX: DEBUG for now */
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ break;
|
|
|
+ case QLA8XXX_DEV_QUIESCENT:
|
|
|
+ /* XXX: DEBUG for now */
|
|
|
+ if (ha->flags.quiesce_owner)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ dev_init_timeout = jiffies +
|
|
|
+ (ha->fcoe_dev_init_timeout * HZ);
|
|
|
+ break;
|
|
|
+ case QLA8XXX_DEV_FAILED:
|
|
|
+ if (ha->flags.nic_core_reset_owner)
|
|
|
+ qla83xx_idc_audit(base_vha,
|
|
|
+ IDC_AUDIT_COMPLETION);
|
|
|
+ ha->flags.nic_core_reset_owner = 0;
|
|
|
+ __qla83xx_clear_drv_presence(base_vha);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ qla8xxx_dev_failed_handler(base_vha);
|
|
|
+ rval = QLA_FUNCTION_FAILED;
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ goto exit;
|
|
|
+ case QLA8XXX_BAD_VALUE:
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ msleep(1000);
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ql_log(ql_log_warn, base_vha, 0xb071,
|
|
|
+ "Unknow Device State: %x.\n", dev_state);
|
|
|
+ qla83xx_idc_unlock(base_vha, 0);
|
|
|
+ qla8xxx_dev_failed_handler(base_vha);
|
|
|
+ rval = QLA_FUNCTION_FAILED;
|
|
|
+ qla83xx_idc_lock(base_vha, 0);
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+exit:
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
/**************************************************************************
|
|
|
* qla2x00_do_dpc
|
|
|
* This kernel thread is a task that is schedule by the interrupt handler
|
|
@@ -3764,7 +4432,7 @@ qla2x00_do_dpc(void *data)
|
|
|
&base_vha->dpc_flags)) {
|
|
|
qla82xx_idc_lock(ha);
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_FAILED);
|
|
|
+ QLA8XXX_DEV_FAILED);
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
ql_log(ql_log_info, base_vha, 0x4004,
|
|
|
"HW State: FAILED.\n");
|
|
@@ -4341,7 +5009,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
|
|
|
qla82xx_idc_lock(ha);
|
|
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_INITIALIZING);
|
|
|
+ QLA8XXX_DEV_INITIALIZING);
|
|
|
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
|
|
|
QLA82XX_IDC_VERSION);
|
|
@@ -4365,12 +5033,12 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
|
|
|
"HW State: FAILED.\n");
|
|
|
qla82xx_clear_drv_active(ha);
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_FAILED);
|
|
|
+ QLA8XXX_DEV_FAILED);
|
|
|
} else {
|
|
|
ql_log(ql_log_info, base_vha, 0x900c,
|
|
|
"HW State: READY.\n");
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
- QLA82XX_DEV_READY);
|
|
|
+ QLA8XXX_DEV_READY);
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
ha->flags.isp82xx_fw_hung = 0;
|
|
|
rval = qla82xx_restart_isp(base_vha);
|
|
@@ -4385,7 +5053,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
|
|
|
"This devfn is not reset owner = 0x%x.\n",
|
|
|
ha->pdev->devfn);
|
|
|
if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
|
|
|
- QLA82XX_DEV_READY)) {
|
|
|
+ QLA8XXX_DEV_READY)) {
|
|
|
ha->flags.isp82xx_fw_hung = 0;
|
|
|
rval = qla82xx_restart_isp(base_vha);
|
|
|
qla82xx_idc_lock(ha);
|