diff options
author | Santosh Vernekar <santosh.vernekar@qlogic.com> | 2012-08-22 14:21:03 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-09-24 04:10:47 -0400 |
commit | 7d613ac6acec8c29e7aa3f80e28e8e982977a151 (patch) | |
tree | eec782c42537c4658850ffb8982973f122e388a2 /drivers/scsi/qla2xxx/qla_os.c | |
parent | 40129a4c6edc1753b9a537877b6a2eac9fc6c659 (diff) |
[SCSI] qla2xxx: IDC implementation for ISP83xx.
Signed-off-by: Santosh Vernekar <santosh.vernekar@qlogic.com>
Signed-off-by: Saurav Kashyap <saurav.kashyap@qlogic.com>
Signed-off-by: Chad Dupuis <chad.dupuis@qlogic.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_os.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 684 |
1 files changed, 676 insertions, 8 deletions
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3e775650c20c..c705a51ee333 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -2149,7 +2149,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
2149 | scsi_qla_host_t *base_vha = NULL; | 2149 | scsi_qla_host_t *base_vha = NULL; |
2150 | struct qla_hw_data *ha; | 2150 | struct qla_hw_data *ha; |
2151 | char pci_info[30]; | 2151 | char pci_info[30]; |
2152 | char fw_str[30]; | 2152 | char fw_str[30], wq_name[30]; |
2153 | struct scsi_host_template *sht; | 2153 | struct scsi_host_template *sht; |
2154 | int bars, mem_only = 0; | 2154 | int bars, mem_only = 0; |
2155 | uint16_t req_length = 0, rsp_length = 0; | 2155 | 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) | |||
2319 | ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; | 2319 | ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; |
2320 | ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; | 2320 | ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; |
2321 | } else if (IS_QLA83XX(ha)) { | 2321 | } else if (IS_QLA83XX(ha)) { |
2322 | ha->portnum = PCI_FUNC(ha->pdev->devfn); | ||
2322 | ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; | 2323 | ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; |
2323 | ha->mbx_count = MAILBOX_REGISTER_COUNT; | 2324 | ha->mbx_count = MAILBOX_REGISTER_COUNT; |
2324 | req_length = REQUEST_ENTRY_CNT_24XX; | 2325 | req_length = REQUEST_ENTRY_CNT_24XX; |
@@ -2403,6 +2404,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
2403 | base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER + | 2404 | base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER + |
2404 | base_vha->vp_idx; | 2405 | base_vha->vp_idx; |
2405 | 2406 | ||
2407 | if (IS_QLA8031(ha)) { | ||
2408 | sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no); | ||
2409 | ha->dpc_lp_wq = create_singlethread_workqueue(wq_name); | ||
2410 | INIT_WORK(&ha->idc_aen, qla83xx_service_idc_aen); | ||
2411 | |||
2412 | sprintf(wq_name, "qla2xxx_%lu_dpc_hp_wq", base_vha->host_no); | ||
2413 | ha->dpc_hp_wq = create_singlethread_workqueue(wq_name); | ||
2414 | INIT_WORK(&ha->nic_core_reset, qla83xx_nic_core_reset_work); | ||
2415 | INIT_WORK(&ha->idc_state_handler, | ||
2416 | qla83xx_idc_state_handler_work); | ||
2417 | INIT_WORK(&ha->nic_core_unrecoverable, | ||
2418 | qla83xx_nic_core_unrecoverable_work); | ||
2419 | } | ||
2420 | |||
2406 | /* Set the SG table size based on ISP type */ | 2421 | /* Set the SG table size based on ISP type */ |
2407 | if (!IS_FWI2_CAPABLE(ha)) { | 2422 | if (!IS_FWI2_CAPABLE(ha)) { |
2408 | if (IS_QLA2100(ha)) | 2423 | if (IS_QLA2100(ha)) |
@@ -2500,7 +2515,7 @@ que_init: | |||
2500 | if (IS_QLA82XX(ha)) { | 2515 | if (IS_QLA82XX(ha)) { |
2501 | qla82xx_idc_lock(ha); | 2516 | qla82xx_idc_lock(ha); |
2502 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | 2517 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, |
2503 | QLA82XX_DEV_FAILED); | 2518 | QLA8XXX_DEV_FAILED); |
2504 | qla82xx_idc_unlock(ha); | 2519 | qla82xx_idc_unlock(ha); |
2505 | ql_log(ql_log_fatal, base_vha, 0x00d7, | 2520 | ql_log(ql_log_fatal, base_vha, 0x00d7, |
2506 | "HW State: FAILED.\n"); | 2521 | "HW State: FAILED.\n"); |
@@ -2751,6 +2766,14 @@ qla2x00_remove_one(struct pci_dev *pdev) | |||
2751 | } | 2766 | } |
2752 | mutex_unlock(&ha->vport_lock); | 2767 | mutex_unlock(&ha->vport_lock); |
2753 | 2768 | ||
2769 | if (IS_QLA8031(ha)) { | ||
2770 | ql_dbg(ql_dbg_p3p, base_vha, 0xb07e, | ||
2771 | "Clearing fcoe driver presence.\n"); | ||
2772 | if (qla83xx_clear_drv_presence(base_vha) != QLA_SUCCESS) | ||
2773 | ql_dbg(ql_dbg_p3p, base_vha, 0xb079, | ||
2774 | "Error while clearing DRV-Presence.\n"); | ||
2775 | } | ||
2776 | |||
2754 | set_bit(UNLOADING, &base_vha->dpc_flags); | 2777 | set_bit(UNLOADING, &base_vha->dpc_flags); |
2755 | 2778 | ||
2756 | qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); | 2779 | qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); |
@@ -2772,6 +2795,21 @@ qla2x00_remove_one(struct pci_dev *pdev) | |||
2772 | ha->wq = NULL; | 2795 | ha->wq = NULL; |
2773 | } | 2796 | } |
2774 | 2797 | ||
2798 | /* Cancel all work and destroy DPC workqueues */ | ||
2799 | if (ha->dpc_lp_wq) { | ||
2800 | cancel_work_sync(&ha->idc_aen); | ||
2801 | destroy_workqueue(ha->dpc_lp_wq); | ||
2802 | ha->dpc_lp_wq = NULL; | ||
2803 | } | ||
2804 | |||
2805 | if (ha->dpc_hp_wq) { | ||
2806 | cancel_work_sync(&ha->nic_core_reset); | ||
2807 | cancel_work_sync(&ha->idc_state_handler); | ||
2808 | cancel_work_sync(&ha->nic_core_unrecoverable); | ||
2809 | destroy_workqueue(ha->dpc_hp_wq); | ||
2810 | ha->dpc_hp_wq = NULL; | ||
2811 | } | ||
2812 | |||
2775 | /* Kill the kernel thread for this host */ | 2813 | /* Kill the kernel thread for this host */ |
2776 | if (ha->dpc_thread) { | 2814 | if (ha->dpc_thread) { |
2777 | struct task_struct *t = ha->dpc_thread; | 2815 | struct task_struct *t = ha->dpc_thread; |
@@ -2838,7 +2876,6 @@ qla2x00_free_device(scsi_qla_host_t *vha) | |||
2838 | qla2x00_stop_dpc_thread(vha); | 2876 | qla2x00_stop_dpc_thread(vha); |
2839 | 2877 | ||
2840 | qla25xx_delete_queues(vha); | 2878 | qla25xx_delete_queues(vha); |
2841 | |||
2842 | if (ha->flags.fce_enabled) | 2879 | if (ha->flags.fce_enabled) |
2843 | qla2x00_disable_fce_trace(vha, NULL, NULL); | 2880 | qla2x00_disable_fce_trace(vha, NULL, NULL); |
2844 | 2881 | ||
@@ -3709,6 +3746,637 @@ void qla2x00_relogin(struct scsi_qla_host *vha) | |||
3709 | } | 3746 | } |
3710 | } | 3747 | } |
3711 | 3748 | ||
3749 | /* Schedule work on any of the dpc-workqueues */ | ||
3750 | void | ||
3751 | qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code) | ||
3752 | { | ||
3753 | struct qla_hw_data *ha = base_vha->hw; | ||
3754 | |||
3755 | switch (work_code) { | ||
3756 | case MBA_IDC_AEN: /* 0x8200 */ | ||
3757 | if (ha->dpc_lp_wq) | ||
3758 | queue_work(ha->dpc_lp_wq, &ha->idc_aen); | ||
3759 | break; | ||
3760 | |||
3761 | case QLA83XX_NIC_CORE_RESET: /* 0x1 */ | ||
3762 | if (!ha->flags.nic_core_reset_hdlr_active) { | ||
3763 | if (ha->dpc_hp_wq) | ||
3764 | queue_work(ha->dpc_hp_wq, &ha->nic_core_reset); | ||
3765 | } else | ||
3766 | ql_dbg(ql_dbg_p3p, base_vha, 0xb05e, | ||
3767 | "NIC Core reset is already active. Skip " | ||
3768 | "scheduling it again.\n"); | ||
3769 | break; | ||
3770 | case QLA83XX_IDC_STATE_HANDLER: /* 0x2 */ | ||
3771 | if (ha->dpc_hp_wq) | ||
3772 | queue_work(ha->dpc_hp_wq, &ha->idc_state_handler); | ||
3773 | break; | ||
3774 | case QLA83XX_NIC_CORE_UNRECOVERABLE: /* 0x3 */ | ||
3775 | if (ha->dpc_hp_wq) | ||
3776 | queue_work(ha->dpc_hp_wq, &ha->nic_core_unrecoverable); | ||
3777 | break; | ||
3778 | default: | ||
3779 | ql_log(ql_log_warn, base_vha, 0xb05f, | ||
3780 | "Unknow work-code=0x%x.\n", work_code); | ||
3781 | } | ||
3782 | |||
3783 | return; | ||
3784 | } | ||
3785 | |||
3786 | /* Work: Perform NIC Core Unrecoverable state handling */ | ||
3787 | void | ||
3788 | qla83xx_nic_core_unrecoverable_work(struct work_struct *work) | ||
3789 | { | ||
3790 | struct qla_hw_data *ha = | ||
3791 | container_of(work, struct qla_hw_data, nic_core_reset); | ||
3792 | scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); | ||
3793 | uint32_t dev_state = 0; | ||
3794 | |||
3795 | qla83xx_idc_lock(base_vha, 0); | ||
3796 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); | ||
3797 | qla83xx_reset_ownership(base_vha); | ||
3798 | if (ha->flags.nic_core_reset_owner) { | ||
3799 | ha->flags.nic_core_reset_owner = 0; | ||
3800 | qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, | ||
3801 | QLA8XXX_DEV_FAILED); | ||
3802 | ql_log(ql_log_info, base_vha, 0xb060, "HW State: FAILED.\n"); | ||
3803 | qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); | ||
3804 | } | ||
3805 | qla83xx_idc_unlock(base_vha, 0); | ||
3806 | } | ||
3807 | |||
3808 | /* Work: Execute IDC state handler */ | ||
3809 | void | ||
3810 | qla83xx_idc_state_handler_work(struct work_struct *work) | ||
3811 | { | ||
3812 | struct qla_hw_data *ha = | ||
3813 | container_of(work, struct qla_hw_data, nic_core_reset); | ||
3814 | scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); | ||
3815 | uint32_t dev_state = 0; | ||
3816 | |||
3817 | qla83xx_idc_lock(base_vha, 0); | ||
3818 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); | ||
3819 | if (dev_state == QLA8XXX_DEV_FAILED || | ||
3820 | dev_state == QLA8XXX_DEV_NEED_QUIESCENT) | ||
3821 | qla83xx_idc_state_handler(base_vha); | ||
3822 | qla83xx_idc_unlock(base_vha, 0); | ||
3823 | } | ||
3824 | |||
3825 | int | ||
3826 | qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha) | ||
3827 | { | ||
3828 | int rval = QLA_SUCCESS; | ||
3829 | unsigned long heart_beat_wait = jiffies + (1 * HZ); | ||
3830 | uint32_t heart_beat_counter1, heart_beat_counter2; | ||
3831 | |||
3832 | do { | ||
3833 | if (time_after(jiffies, heart_beat_wait)) { | ||
3834 | ql_dbg(ql_dbg_p3p, base_vha, 0xb07c, | ||
3835 | "Nic Core f/w is not alive.\n"); | ||
3836 | rval = QLA_FUNCTION_FAILED; | ||
3837 | break; | ||
3838 | } | ||
3839 | |||
3840 | qla83xx_idc_lock(base_vha, 0); | ||
3841 | qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, | ||
3842 | &heart_beat_counter1); | ||
3843 | qla83xx_idc_unlock(base_vha, 0); | ||
3844 | msleep(100); | ||
3845 | qla83xx_idc_lock(base_vha, 0); | ||
3846 | qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, | ||
3847 | &heart_beat_counter2); | ||
3848 | qla83xx_idc_unlock(base_vha, 0); | ||
3849 | } while (heart_beat_counter1 == heart_beat_counter2); | ||
3850 | |||
3851 | return rval; | ||
3852 | } | ||
3853 | |||
3854 | /* Work: Perform NIC Core Reset handling */ | ||
3855 | void | ||
3856 | qla83xx_nic_core_reset_work(struct work_struct *work) | ||
3857 | { | ||
3858 | struct qla_hw_data *ha = | ||
3859 | container_of(work, struct qla_hw_data, nic_core_reset); | ||
3860 | scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); | ||
3861 | uint32_t dev_state = 0; | ||
3862 | |||
3863 | if (!ha->flags.nic_core_reset_hdlr_active) { | ||
3864 | if (qla83xx_check_nic_core_fw_alive(base_vha) == QLA_SUCCESS) { | ||
3865 | qla83xx_idc_lock(base_vha, 0); | ||
3866 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, | ||
3867 | &dev_state); | ||
3868 | qla83xx_idc_unlock(base_vha, 0); | ||
3869 | if (dev_state != QLA8XXX_DEV_NEED_RESET) { | ||
3870 | ql_dbg(ql_dbg_p3p, base_vha, 0xb07a, | ||
3871 | "Nic Core f/w is alive.\n"); | ||
3872 | return; | ||
3873 | } | ||
3874 | } | ||
3875 | |||
3876 | ha->flags.nic_core_reset_hdlr_active = 1; | ||
3877 | if (qla83xx_nic_core_reset(base_vha)) { | ||
3878 | /* NIC Core reset failed. */ | ||
3879 | ql_dbg(ql_dbg_p3p, base_vha, 0xb061, | ||
3880 | "NIC Core reset failed.\n"); | ||
3881 | } | ||
3882 | ha->flags.nic_core_reset_hdlr_active = 0; | ||
3883 | } | ||
3884 | } | ||
3885 | |||
3886 | /* Work: Handle 8200 IDC aens */ | ||
3887 | void | ||
3888 | qla83xx_service_idc_aen(struct work_struct *work) | ||
3889 | { | ||
3890 | struct qla_hw_data *ha = | ||
3891 | container_of(work, struct qla_hw_data, idc_aen); | ||
3892 | scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); | ||
3893 | uint32_t dev_state, idc_control; | ||
3894 | |||
3895 | qla83xx_idc_lock(base_vha, 0); | ||
3896 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); | ||
3897 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_CONTROL, &idc_control); | ||
3898 | qla83xx_idc_unlock(base_vha, 0); | ||
3899 | if (dev_state == QLA8XXX_DEV_NEED_RESET) { | ||
3900 | if (idc_control & QLA83XX_IDC_GRACEFUL_RESET) { | ||
3901 | ql_dbg(ql_dbg_p3p, base_vha, 0xb062, | ||
3902 | "Application requested NIC Core Reset.\n"); | ||
3903 | qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); | ||
3904 | } else if (qla83xx_check_nic_core_fw_alive(base_vha) == | ||
3905 | QLA_SUCCESS) { | ||
3906 | ql_dbg(ql_dbg_p3p, base_vha, 0xb07b, | ||
3907 | "Other protocol driver requested NIC Core Reset.\n"); | ||
3908 | qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); | ||
3909 | } | ||
3910 | } else if (dev_state == QLA8XXX_DEV_FAILED || | ||
3911 | dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { | ||
3912 | qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); | ||
3913 | } | ||
3914 | } | ||
3915 | |||
3916 | static void | ||
3917 | qla83xx_wait_logic(void) | ||
3918 | { | ||
3919 | int i; | ||
3920 | |||
3921 | /* Yield CPU */ | ||
3922 | if (!in_interrupt()) { | ||
3923 | /* | ||
3924 | * Wait about 200ms before retrying again. | ||
3925 | * This controls the number of retries for single | ||
3926 | * lock operation. | ||
3927 | */ | ||
3928 | msleep(100); | ||
3929 | schedule(); | ||
3930 | } else { | ||
3931 | for (i = 0; i < 20; i++) | ||
3932 | cpu_relax(); /* This a nop instr on i386 */ | ||
3933 | } | ||
3934 | } | ||
3935 | |||
3936 | int | ||
3937 | qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha) | ||
3938 | { | ||
3939 | int rval; | ||
3940 | uint32_t data; | ||
3941 | uint32_t idc_lck_rcvry_stage_mask = 0x3; | ||
3942 | uint32_t idc_lck_rcvry_owner_mask = 0x3c; | ||
3943 | struct qla_hw_data *ha = base_vha->hw; | ||
3944 | |||
3945 | rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, &data); | ||
3946 | if (rval) | ||
3947 | return rval; | ||
3948 | |||
3949 | if ((data & idc_lck_rcvry_stage_mask) > 0) { | ||
3950 | return QLA_SUCCESS; | ||
3951 | } else { | ||
3952 | data = (IDC_LOCK_RECOVERY_STAGE1) | (ha->portnum << 2); | ||
3953 | rval = qla83xx_wr_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, | ||
3954 | data); | ||
3955 | if (rval) | ||
3956 | return rval; | ||
3957 | |||
3958 | msleep(200); | ||
3959 | |||
3960 | rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, | ||
3961 | &data); | ||
3962 | if (rval) | ||
3963 | return rval; | ||
3964 | |||
3965 | if (((data & idc_lck_rcvry_owner_mask) >> 2) == ha->portnum) { | ||
3966 | data &= (IDC_LOCK_RECOVERY_STAGE2 | | ||
3967 | ~(idc_lck_rcvry_stage_mask)); | ||
3968 | rval = qla83xx_wr_reg(base_vha, | ||
3969 | QLA83XX_IDC_LOCK_RECOVERY, data); | ||
3970 | if (rval) | ||
3971 | return rval; | ||
3972 | |||
3973 | /* Forcefully perform IDC UnLock */ | ||
3974 | rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, | ||
3975 | &data); | ||
3976 | if (rval) | ||
3977 | return rval; | ||
3978 | /* Clear lock-id by setting 0xff */ | ||
3979 | rval = qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, | ||
3980 | 0xff); | ||
3981 | if (rval) | ||
3982 | return rval; | ||
3983 | /* Clear lock-recovery by setting 0x0 */ | ||
3984 | rval = qla83xx_wr_reg(base_vha, | ||
3985 | QLA83XX_IDC_LOCK_RECOVERY, 0x0); | ||
3986 | if (rval) | ||
3987 | return rval; | ||
3988 | } else | ||
3989 | return QLA_SUCCESS; | ||
3990 | } | ||
3991 | |||
3992 | return rval; | ||
3993 | } | ||
3994 | |||
3995 | int | ||
3996 | qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha) | ||
3997 | { | ||
3998 | int rval = QLA_SUCCESS; | ||
3999 | uint32_t o_drv_lockid, n_drv_lockid; | ||
4000 | unsigned long lock_recovery_timeout; | ||
4001 | |||
4002 | lock_recovery_timeout = jiffies + QLA83XX_MAX_LOCK_RECOVERY_WAIT; | ||
4003 | retry_lockid: | ||
4004 | rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &o_drv_lockid); | ||
4005 | if (rval) | ||
4006 | goto exit; | ||
4007 | |||
4008 | /* MAX wait time before forcing IDC Lock recovery = 2 secs */ | ||
4009 | if (time_after_eq(jiffies, lock_recovery_timeout)) { | ||
4010 | if (qla83xx_force_lock_recovery(base_vha) == QLA_SUCCESS) | ||
4011 | return QLA_SUCCESS; | ||
4012 | else | ||
4013 | return QLA_FUNCTION_FAILED; | ||
4014 | } | ||
4015 | |||
4016 | rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &n_drv_lockid); | ||
4017 | if (rval) | ||
4018 | goto exit; | ||
4019 | |||
4020 | if (o_drv_lockid == n_drv_lockid) { | ||
4021 | qla83xx_wait_logic(); | ||
4022 | goto retry_lockid; | ||
4023 | } else | ||
4024 | return QLA_SUCCESS; | ||
4025 | |||
4026 | exit: | ||
4027 | return rval; | ||
4028 | } | ||
4029 | |||
4030 | void | ||
4031 | qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id) | ||
4032 | { | ||
4033 | uint16_t options = (requester_id << 15) | BIT_6; | ||
4034 | uint32_t data; | ||
4035 | struct qla_hw_data *ha = base_vha->hw; | ||
4036 | |||
4037 | /* IDC-lock implementation using driver-lock/lock-id remote registers */ | ||
4038 | retry_lock: | ||
4039 | if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data) | ||
4040 | == QLA_SUCCESS) { | ||
4041 | if (data) { | ||
4042 | /* Setting lock-id to our function-number */ | ||
4043 | qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, | ||
4044 | ha->portnum); | ||
4045 | } else { | ||
4046 | ql_dbg(ql_dbg_p3p, base_vha, 0xb063, | ||
4047 | "Failed to acquire IDC lock. retrying...\n"); | ||
4048 | |||
4049 | /* Retry/Perform IDC-Lock recovery */ | ||
4050 | if (qla83xx_idc_lock_recovery(base_vha) | ||
4051 | == QLA_SUCCESS) { | ||
4052 | qla83xx_wait_logic(); | ||
4053 | goto retry_lock; | ||
4054 | } else | ||
4055 | ql_log(ql_log_warn, base_vha, 0xb075, | ||
4056 | "IDC Lock recovery FAILED.\n"); | ||
4057 | } | ||
4058 | |||
4059 | } | ||
4060 | |||
4061 | return; | ||
4062 | |||
4063 | /* XXX: IDC-lock implementation using access-control mbx */ | ||
4064 | retry_lock2: | ||
4065 | if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { | ||
4066 | ql_dbg(ql_dbg_p3p, base_vha, 0xb072, | ||
4067 | "Failed to acquire IDC lock. retrying...\n"); | ||
4068 | /* Retry/Perform IDC-Lock recovery */ | ||
4069 | if (qla83xx_idc_lock_recovery(base_vha) == QLA_SUCCESS) { | ||
4070 | qla83xx_wait_logic(); | ||
4071 | goto retry_lock2; | ||
4072 | } else | ||
4073 | ql_log(ql_log_warn, base_vha, 0xb076, | ||
4074 | "IDC Lock recovery FAILED.\n"); | ||
4075 | } | ||
4076 | |||
4077 | return; | ||
4078 | } | ||
4079 | |||
4080 | void | ||
4081 | qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id) | ||
4082 | { | ||
4083 | uint16_t options = (requester_id << 15) | BIT_7, retry; | ||
4084 | uint32_t data; | ||
4085 | struct qla_hw_data *ha = base_vha->hw; | ||
4086 | |||
4087 | /* IDC-unlock implementation using driver-unlock/lock-id | ||
4088 | * remote registers | ||
4089 | */ | ||
4090 | retry = 0; | ||
4091 | retry_unlock: | ||
4092 | if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &data) | ||
4093 | == QLA_SUCCESS) { | ||
4094 | if (data == ha->portnum) { | ||
4095 | qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, &data); | ||
4096 | /* Clearing lock-id by setting 0xff */ | ||
4097 | qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, 0xff); | ||
4098 | } else if (retry < 10) { | ||
4099 | /* SV: XXX: IDC unlock retrying needed here? */ | ||
4100 | |||
4101 | /* Retry for IDC-unlock */ | ||
4102 | qla83xx_wait_logic(); | ||
4103 | retry++; | ||
4104 | ql_dbg(ql_dbg_p3p, base_vha, 0xb064, | ||
4105 | "Failed to release IDC lock, retyring=%d\n", retry); | ||
4106 | goto retry_unlock; | ||
4107 | } | ||
4108 | } else if (retry < 10) { | ||
4109 | /* Retry for IDC-unlock */ | ||
4110 | qla83xx_wait_logic(); | ||
4111 | retry++; | ||
4112 | ql_dbg(ql_dbg_p3p, base_vha, 0xb065, | ||
4113 | "Failed to read drv-lockid, retyring=%d\n", retry); | ||
4114 | goto retry_unlock; | ||
4115 | } | ||
4116 | |||
4117 | return; | ||
4118 | |||
4119 | /* XXX: IDC-unlock implementation using access-control mbx */ | ||
4120 | retry = 0; | ||
4121 | retry_unlock2: | ||
4122 | if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { | ||
4123 | if (retry < 10) { | ||
4124 | /* Retry for IDC-unlock */ | ||
4125 | qla83xx_wait_logic(); | ||
4126 | retry++; | ||
4127 | ql_dbg(ql_dbg_p3p, base_vha, 0xb066, | ||
4128 | "Failed to release IDC lock, retyring=%d\n", retry); | ||
4129 | goto retry_unlock2; | ||
4130 | } | ||
4131 | } | ||
4132 | |||
4133 | return; | ||
4134 | } | ||
4135 | |||
4136 | int | ||
4137 | __qla83xx_set_drv_presence(scsi_qla_host_t *vha) | ||
4138 | { | ||
4139 | int rval = QLA_SUCCESS; | ||
4140 | struct qla_hw_data *ha = vha->hw; | ||
4141 | uint32_t drv_presence; | ||
4142 | |||
4143 | rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); | ||
4144 | if (rval == QLA_SUCCESS) { | ||
4145 | drv_presence |= (1 << ha->portnum); | ||
4146 | rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, | ||
4147 | drv_presence); | ||
4148 | } | ||
4149 | |||
4150 | return rval; | ||
4151 | } | ||
4152 | |||
4153 | int | ||
4154 | qla83xx_set_drv_presence(scsi_qla_host_t *vha) | ||
4155 | { | ||
4156 | int rval = QLA_SUCCESS; | ||
4157 | |||
4158 | qla83xx_idc_lock(vha, 0); | ||
4159 | rval = __qla83xx_set_drv_presence(vha); | ||
4160 | qla83xx_idc_unlock(vha, 0); | ||
4161 | |||
4162 | return rval; | ||
4163 | } | ||
4164 | |||
4165 | int | ||
4166 | __qla83xx_clear_drv_presence(scsi_qla_host_t *vha) | ||
4167 | { | ||
4168 | int rval = QLA_SUCCESS; | ||
4169 | struct qla_hw_data *ha = vha->hw; | ||
4170 | uint32_t drv_presence; | ||
4171 | |||
4172 | rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); | ||
4173 | if (rval == QLA_SUCCESS) { | ||
4174 | drv_presence &= ~(1 << ha->portnum); | ||
4175 | rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, | ||
4176 | drv_presence); | ||
4177 | } | ||
4178 | |||
4179 | return rval; | ||
4180 | } | ||
4181 | |||
4182 | int | ||
4183 | qla83xx_clear_drv_presence(scsi_qla_host_t *vha) | ||
4184 | { | ||
4185 | int rval = QLA_SUCCESS; | ||
4186 | |||
4187 | qla83xx_idc_lock(vha, 0); | ||
4188 | rval = __qla83xx_clear_drv_presence(vha); | ||
4189 | qla83xx_idc_unlock(vha, 0); | ||
4190 | |||
4191 | return rval; | ||
4192 | } | ||
4193 | |||
4194 | void | ||
4195 | qla83xx_need_reset_handler(scsi_qla_host_t *vha) | ||
4196 | { | ||
4197 | struct qla_hw_data *ha = vha->hw; | ||
4198 | uint32_t drv_ack, drv_presence; | ||
4199 | unsigned long ack_timeout; | ||
4200 | |||
4201 | /* Wait for IDC ACK from all functions (DRV-ACK == DRV-PRESENCE) */ | ||
4202 | ack_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); | ||
4203 | while (1) { | ||
4204 | qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); | ||
4205 | qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); | ||
4206 | if (drv_ack == drv_presence) | ||
4207 | break; | ||
4208 | |||
4209 | if (time_after_eq(jiffies, ack_timeout)) { | ||
4210 | ql_log(ql_log_warn, vha, 0xb067, | ||
4211 | "RESET ACK TIMEOUT! drv_presence=0x%x " | ||
4212 | "drv_ack=0x%x\n", drv_presence, drv_ack); | ||
4213 | /* | ||
4214 | * The function(s) which did not ack in time are forced | ||
4215 | * to withdraw any further participation in the IDC | ||
4216 | * reset. | ||
4217 | */ | ||
4218 | if (drv_ack != drv_presence) | ||
4219 | qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, | ||
4220 | drv_ack); | ||
4221 | break; | ||
4222 | } | ||
4223 | |||
4224 | qla83xx_idc_unlock(vha, 0); | ||
4225 | msleep(1000); | ||
4226 | qla83xx_idc_lock(vha, 0); | ||
4227 | } | ||
4228 | |||
4229 | qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_COLD); | ||
4230 | ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n"); | ||
4231 | } | ||
4232 | |||
4233 | int | ||
4234 | qla83xx_device_bootstrap(scsi_qla_host_t *vha) | ||
4235 | { | ||
4236 | int rval = QLA_SUCCESS; | ||
4237 | uint32_t idc_control; | ||
4238 | |||
4239 | qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_INITIALIZING); | ||
4240 | ql_log(ql_log_info, vha, 0xb069, "HW State: INITIALIZING.\n"); | ||
4241 | |||
4242 | /* Clearing IDC-Control Graceful-Reset Bit before resetting f/w */ | ||
4243 | __qla83xx_get_idc_control(vha, &idc_control); | ||
4244 | idc_control &= ~QLA83XX_IDC_GRACEFUL_RESET; | ||
4245 | __qla83xx_set_idc_control(vha, 0); | ||
4246 | |||
4247 | qla83xx_idc_unlock(vha, 0); | ||
4248 | rval = qla83xx_restart_nic_firmware(vha); | ||
4249 | qla83xx_idc_lock(vha, 0); | ||
4250 | |||
4251 | if (rval != QLA_SUCCESS) { | ||
4252 | ql_log(ql_log_fatal, vha, 0xb06a, | ||
4253 | "Failed to restart NIC f/w.\n"); | ||
4254 | qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_FAILED); | ||
4255 | ql_log(ql_log_info, vha, 0xb06b, "HW State: FAILED.\n"); | ||
4256 | } else { | ||
4257 | ql_dbg(ql_dbg_p3p, vha, 0xb06c, | ||
4258 | "Success in restarting nic f/w.\n"); | ||
4259 | qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_READY); | ||
4260 | ql_log(ql_log_info, vha, 0xb06d, "HW State: READY.\n"); | ||
4261 | } | ||
4262 | |||
4263 | return rval; | ||
4264 | } | ||
4265 | |||
4266 | /* Assumes idc_lock always held on entry */ | ||
4267 | int | ||
4268 | qla83xx_idc_state_handler(scsi_qla_host_t *base_vha) | ||
4269 | { | ||
4270 | struct qla_hw_data *ha = base_vha->hw; | ||
4271 | int rval = QLA_SUCCESS; | ||
4272 | unsigned long dev_init_timeout; | ||
4273 | uint32_t dev_state; | ||
4274 | |||
4275 | /* Wait for MAX-INIT-TIMEOUT for the device to go ready */ | ||
4276 | dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); | ||
4277 | |||
4278 | while (1) { | ||
4279 | |||
4280 | if (time_after_eq(jiffies, dev_init_timeout)) { | ||
4281 | ql_log(ql_log_warn, base_vha, 0xb06e, | ||
4282 | "Initialization TIMEOUT!\n"); | ||
4283 | /* Init timeout. Disable further NIC Core | ||
4284 | * communication. | ||
4285 | */ | ||
4286 | qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, | ||
4287 | QLA8XXX_DEV_FAILED); | ||
4288 | ql_log(ql_log_info, base_vha, 0xb06f, | ||
4289 | "HW State: FAILED.\n"); | ||
4290 | } | ||
4291 | |||
4292 | qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); | ||
4293 | switch (dev_state) { | ||
4294 | case QLA8XXX_DEV_READY: | ||
4295 | if (ha->flags.nic_core_reset_owner) | ||
4296 | qla83xx_idc_audit(base_vha, | ||
4297 | IDC_AUDIT_COMPLETION); | ||
4298 | ha->flags.nic_core_reset_owner = 0; | ||
4299 | ql_dbg(ql_dbg_p3p, base_vha, 0xb070, | ||
4300 | "Reset_owner reset by 0x%x.\n", | ||
4301 | ha->portnum); | ||
4302 | goto exit; | ||
4303 | case QLA8XXX_DEV_COLD: | ||
4304 | if (ha->flags.nic_core_reset_owner) | ||
4305 | rval = qla83xx_device_bootstrap(base_vha); | ||
4306 | else { | ||
4307 | /* Wait for AEN to change device-state */ | ||
4308 | qla83xx_idc_unlock(base_vha, 0); | ||
4309 | msleep(1000); | ||
4310 | qla83xx_idc_lock(base_vha, 0); | ||
4311 | } | ||
4312 | break; | ||
4313 | case QLA8XXX_DEV_INITIALIZING: | ||
4314 | /* Wait for AEN to change device-state */ | ||
4315 | qla83xx_idc_unlock(base_vha, 0); | ||
4316 | msleep(1000); | ||
4317 | qla83xx_idc_lock(base_vha, 0); | ||
4318 | break; | ||
4319 | case QLA8XXX_DEV_NEED_RESET: | ||
4320 | if (!ql2xdontresethba && ha->flags.nic_core_reset_owner) | ||
4321 | qla83xx_need_reset_handler(base_vha); | ||
4322 | else { | ||
4323 | /* Wait for AEN to change device-state */ | ||
4324 | qla83xx_idc_unlock(base_vha, 0); | ||
4325 | msleep(1000); | ||
4326 | qla83xx_idc_lock(base_vha, 0); | ||
4327 | } | ||
4328 | /* reset timeout value after need reset handler */ | ||
4329 | dev_init_timeout = jiffies + | ||
4330 | (ha->fcoe_dev_init_timeout * HZ); | ||
4331 | break; | ||
4332 | case QLA8XXX_DEV_NEED_QUIESCENT: | ||
4333 | /* XXX: DEBUG for now */ | ||
4334 | qla83xx_idc_unlock(base_vha, 0); | ||
4335 | msleep(1000); | ||
4336 | qla83xx_idc_lock(base_vha, 0); | ||
4337 | break; | ||
4338 | case QLA8XXX_DEV_QUIESCENT: | ||
4339 | /* XXX: DEBUG for now */ | ||
4340 | if (ha->flags.quiesce_owner) | ||
4341 | goto exit; | ||
4342 | |||
4343 | qla83xx_idc_unlock(base_vha, 0); | ||
4344 | msleep(1000); | ||
4345 | qla83xx_idc_lock(base_vha, 0); | ||
4346 | dev_init_timeout = jiffies + | ||
4347 | (ha->fcoe_dev_init_timeout * HZ); | ||
4348 | break; | ||
4349 | case QLA8XXX_DEV_FAILED: | ||
4350 | if (ha->flags.nic_core_reset_owner) | ||
4351 | qla83xx_idc_audit(base_vha, | ||
4352 | IDC_AUDIT_COMPLETION); | ||
4353 | ha->flags.nic_core_reset_owner = 0; | ||
4354 | __qla83xx_clear_drv_presence(base_vha); | ||
4355 | qla83xx_idc_unlock(base_vha, 0); | ||
4356 | qla8xxx_dev_failed_handler(base_vha); | ||
4357 | rval = QLA_FUNCTION_FAILED; | ||
4358 | qla83xx_idc_lock(base_vha, 0); | ||
4359 | goto exit; | ||
4360 | case QLA8XXX_BAD_VALUE: | ||
4361 | qla83xx_idc_unlock(base_vha, 0); | ||
4362 | msleep(1000); | ||
4363 | qla83xx_idc_lock(base_vha, 0); | ||
4364 | break; | ||
4365 | default: | ||
4366 | ql_log(ql_log_warn, base_vha, 0xb071, | ||
4367 | "Unknow Device State: %x.\n", dev_state); | ||
4368 | qla83xx_idc_unlock(base_vha, 0); | ||
4369 | qla8xxx_dev_failed_handler(base_vha); | ||
4370 | rval = QLA_FUNCTION_FAILED; | ||
4371 | qla83xx_idc_lock(base_vha, 0); | ||
4372 | goto exit; | ||
4373 | } | ||
4374 | } | ||
4375 | |||
4376 | exit: | ||
4377 | return rval; | ||
4378 | } | ||
4379 | |||
3712 | /************************************************************************** | 4380 | /************************************************************************** |
3713 | * qla2x00_do_dpc | 4381 | * qla2x00_do_dpc |
3714 | * This kernel thread is a task that is schedule by the interrupt handler | 4382 | * This kernel thread is a task that is schedule by the interrupt handler |
@@ -3764,7 +4432,7 @@ qla2x00_do_dpc(void *data) | |||
3764 | &base_vha->dpc_flags)) { | 4432 | &base_vha->dpc_flags)) { |
3765 | qla82xx_idc_lock(ha); | 4433 | qla82xx_idc_lock(ha); |
3766 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | 4434 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, |
3767 | QLA82XX_DEV_FAILED); | 4435 | QLA8XXX_DEV_FAILED); |
3768 | qla82xx_idc_unlock(ha); | 4436 | qla82xx_idc_unlock(ha); |
3769 | ql_log(ql_log_info, base_vha, 0x4004, | 4437 | ql_log(ql_log_info, base_vha, 0x4004, |
3770 | "HW State: FAILED.\n"); | 4438 | "HW State: FAILED.\n"); |
@@ -4341,7 +5009,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) | |||
4341 | qla82xx_idc_lock(ha); | 5009 | qla82xx_idc_lock(ha); |
4342 | 5010 | ||
4343 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | 5011 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, |
4344 | QLA82XX_DEV_INITIALIZING); | 5012 | QLA8XXX_DEV_INITIALIZING); |
4345 | 5013 | ||
4346 | qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, | 5014 | qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, |
4347 | QLA82XX_IDC_VERSION); | 5015 | QLA82XX_IDC_VERSION); |
@@ -4365,12 +5033,12 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) | |||
4365 | "HW State: FAILED.\n"); | 5033 | "HW State: FAILED.\n"); |
4366 | qla82xx_clear_drv_active(ha); | 5034 | qla82xx_clear_drv_active(ha); |
4367 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | 5035 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, |
4368 | QLA82XX_DEV_FAILED); | 5036 | QLA8XXX_DEV_FAILED); |
4369 | } else { | 5037 | } else { |
4370 | ql_log(ql_log_info, base_vha, 0x900c, | 5038 | ql_log(ql_log_info, base_vha, 0x900c, |
4371 | "HW State: READY.\n"); | 5039 | "HW State: READY.\n"); |
4372 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | 5040 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, |
4373 | QLA82XX_DEV_READY); | 5041 | QLA8XXX_DEV_READY); |
4374 | qla82xx_idc_unlock(ha); | 5042 | qla82xx_idc_unlock(ha); |
4375 | ha->flags.isp82xx_fw_hung = 0; | 5043 | ha->flags.isp82xx_fw_hung = 0; |
4376 | rval = qla82xx_restart_isp(base_vha); | 5044 | rval = qla82xx_restart_isp(base_vha); |
@@ -4385,7 +5053,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) | |||
4385 | "This devfn is not reset owner = 0x%x.\n", | 5053 | "This devfn is not reset owner = 0x%x.\n", |
4386 | ha->pdev->devfn); | 5054 | ha->pdev->devfn); |
4387 | if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == | 5055 | if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == |
4388 | QLA82XX_DEV_READY)) { | 5056 | QLA8XXX_DEV_READY)) { |
4389 | ha->flags.isp82xx_fw_hung = 0; | 5057 | ha->flags.isp82xx_fw_hung = 0; |
4390 | rval = qla82xx_restart_isp(base_vha); | 5058 | rval = qla82xx_restart_isp(base_vha); |
4391 | qla82xx_idc_lock(ha); | 5059 | qla82xx_idc_lock(ha); |