|
@@ -2343,6 +2343,17 @@ qla82xx_set_qsnt_ready(struct qla_hw_data *ha)
|
|
|
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state);
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+qla82xx_clear_qsnt_ready(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t qsnt_state;
|
|
|
+
|
|
|
+ qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
+ qsnt_state &= ~(QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4));
|
|
|
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state);
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
qla82xx_load_fw(scsi_qla_host_t *vha)
|
|
|
{
|
|
@@ -3261,6 +3272,104 @@ dev_ready:
|
|
|
return QLA_SUCCESS;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+* qla82xx_need_qsnt_handler
|
|
|
+* Code to start quiescence sequence
|
|
|
+*
|
|
|
+* Note:
|
|
|
+* IDC lock must be held upon entry
|
|
|
+*
|
|
|
+* Return: void
|
|
|
+*/
|
|
|
+
|
|
|
+static void
|
|
|
+qla82xx_need_qsnt_handler(scsi_qla_host_t *vha)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t dev_state, drv_state, drv_active;
|
|
|
+ unsigned long reset_timeout;
|
|
|
+
|
|
|
+ if (vha->flags.online) {
|
|
|
+ /*Block any further I/O and wait for pending cmnds to complete*/
|
|
|
+ qla82xx_quiescent_state_cleanup(vha);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Set the quiescence ready bit */
|
|
|
+ qla82xx_set_qsnt_ready(ha);
|
|
|
+
|
|
|
+ /*wait for 30 secs for other functions to ack */
|
|
|
+ reset_timeout = jiffies + (30 * HZ);
|
|
|
+
|
|
|
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
|
|
+ /* Its 2 that is written when qsnt is acked, moving one bit */
|
|
|
+ drv_active = drv_active << 0x01;
|
|
|
+
|
|
|
+ while (drv_state != drv_active) {
|
|
|
+
|
|
|
+ if (time_after_eq(jiffies, reset_timeout)) {
|
|
|
+ /* quiescence timeout, other functions didn't ack
|
|
|
+ * changing the state to DEV_READY
|
|
|
+ */
|
|
|
+ qla_printk(KERN_INFO, ha,
|
|
|
+ "%s: QUIESCENT TIMEOUT\n", QLA2XXX_DRIVER_NAME);
|
|
|
+ qla_printk(KERN_INFO, ha,
|
|
|
+ "DRV_ACTIVE:%d DRV_STATE:%d\n", drv_active,
|
|
|
+ drv_state);
|
|
|
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
|
|
|
+ QLA82XX_DEV_READY);
|
|
|
+ qla_printk(KERN_INFO, ha,
|
|
|
+ "HW State: DEV_READY\n");
|
|
|
+ qla82xx_idc_unlock(ha);
|
|
|
+ qla2x00_perform_loop_resync(vha);
|
|
|
+ qla82xx_idc_lock(ha);
|
|
|
+
|
|
|
+ qla82xx_clear_qsnt_ready(vha);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ qla82xx_idc_unlock(ha);
|
|
|
+ msleep(1000);
|
|
|
+ qla82xx_idc_lock(ha);
|
|
|
+
|
|
|
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
|
|
|
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
|
|
|
+ drv_active = drv_active << 0x01;
|
|
|
+ }
|
|
|
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
+ /* everyone acked so set the state to DEV_QUIESCENCE */
|
|
|
+ if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) {
|
|
|
+ qla_printk(KERN_INFO, ha, "HW State: DEV_QUIESCENT\n");
|
|
|
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+* qla82xx_wait_for_state_change
|
|
|
+* Wait for device state to change from given current state
|
|
|
+*
|
|
|
+* Note:
|
|
|
+* IDC lock must not be held upon entry
|
|
|
+*
|
|
|
+* Return:
|
|
|
+* Changed device state.
|
|
|
+*/
|
|
|
+uint32_t
|
|
|
+qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state)
|
|
|
+{
|
|
|
+ struct qla_hw_data *ha = vha->hw;
|
|
|
+ uint32_t dev_state;
|
|
|
+
|
|
|
+ do {
|
|
|
+ msleep(1000);
|
|
|
+ qla82xx_idc_lock(ha);
|
|
|
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
|
|
|
+ qla82xx_idc_unlock(ha);
|
|
|
+ } while (dev_state == curr_state);
|
|
|
+
|
|
|
+ return dev_state;
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
qla82xx_dev_failed_handler(scsi_qla_host_t *vha)
|
|
|
{
|
|
@@ -3443,11 +3552,25 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
|
|
|
qla82xx_need_reset_handler(vha);
|
|
|
break;
|
|
|
case QLA82XX_DEV_NEED_QUIESCENT:
|
|
|
- qla82xx_set_qsnt_ready(ha);
|
|
|
+ qla82xx_need_qsnt_handler(vha);
|
|
|
+ /* Reset timeout value after quiescence handler */
|
|
|
+ dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\
|
|
|
+ * HZ);
|
|
|
+ break;
|
|
|
case QLA82XX_DEV_QUIESCENT:
|
|
|
+ /* Owner will exit and other will wait for the state
|
|
|
+ * to get changed
|
|
|
+ */
|
|
|
+ if (ha->flags.quiesce_owner)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
qla82xx_idc_unlock(ha);
|
|
|
msleep(1000);
|
|
|
qla82xx_idc_lock(ha);
|
|
|
+
|
|
|
+ /* Reset timeout value after quiescence handler */
|
|
|
+ dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\
|
|
|
+ * HZ);
|
|
|
break;
|
|
|
case QLA82XX_DEV_FAILED:
|
|
|
qla82xx_dev_failed_handler(vha);
|
|
@@ -3490,6 +3613,13 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
|
|
|
&ha->mbx_cmd_flags))
|
|
|
complete(&ha->mbx_intr_comp);
|
|
|
}
|
|
|
+ } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
|
|
|
+ !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
|
|
|
+ DEBUG(qla_printk(KERN_INFO, ha,
|
|
|
+ "scsi(%ld) %s - detected quiescence needed\n",
|
|
|
+ vha->host_no, __func__));
|
|
|
+ set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
|
|
|
+ qla2xxx_wake_dpc(vha);
|
|
|
} else {
|
|
|
qla82xx_check_fw_alive(vha);
|
|
|
}
|