[SCSI] qla2xxx: Added support for quiescence mode for ISP82xx.
[pandora-kernel.git] / drivers / scsi / qla2xxx / qla_nx.c
index ae2acac..9175e84 100644 (file)
@@ -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);
                }