From: Santosh Vernekar Date: Wed, 22 Aug 2012 18:21:03 +0000 (-0400) Subject: [SCSI] qla2xxx: IDC implementation for ISP83xx. X-Git-Tag: next-20120917~76^2^2~33 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=23b36ce4ca9627fce6c60e875d3a8c90a1f43449;p=karo-tx-linux.git [SCSI] qla2xxx: IDC implementation for ISP83xx. Signed-off-by: Santosh Vernekar Signed-off-by: Saurav Kashyap Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index da804f487f21..f76424ef05b0 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -564,6 +564,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int type; + uint32_t idc_control; if (off != 0) return -EINVAL; @@ -587,22 +588,36 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, scsi_unblock_requests(vha->host); break; case 0x2025d: - if (!IS_QLA81XX(ha) || !IS_QLA8031(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha)) return -EPERM; ql_log(ql_log_info, vha, 0x706f, "Issuing MPI reset.\n"); - /* Make sure FC side is not in reset */ - qla2x00_wait_for_hba_online(vha); - - /* Issue MPI reset */ - scsi_block_requests(vha->host); - if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) - ql_log(ql_log_warn, vha, 0x7070, - "MPI reset failed.\n"); - scsi_unblock_requests(vha->host); - break; + if (IS_QLA83XX(ha)) { + uint32_t idc_control; + + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_GRACEFUL_RESET; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + qla83xx_idc_unlock(vha, 0); + break; + } else { + /* Make sure FC side is not in reset */ + qla2x00_wait_for_hba_online(vha); + + /* Issue MPI reset */ + scsi_block_requests(vha->host); + if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x7070, + "MPI reset failed.\n"); + scsi_unblock_requests(vha->host); + break; + } case 0x2025e: if (!IS_QLA82XX(ha) || vha != base_vha) { ql_log(ql_log_info, vha, 0x7071, @@ -616,6 +631,29 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, qla2xxx_wake_dpc(vha); qla2x00_wait_for_fcoe_ctx_reset(vha); break; + case 0x2025f: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bc, + "Disabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + case 0x20260: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bd, + "Enabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control &= ~QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + } return count; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index dac3427ebf24..6da13e26ccac 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1364,7 +1364,7 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; - if (ha->flags.isp82xx_reset_hdlr_active) + if (ha->flags.nic_core_reset_hdlr_active) return -EBUSY; rval = qla2x00_optrom_setup(bsg_job, vha, 0); diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 156f5341a7a3..55998f4fb3ae 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -11,18 +11,18 @@ * ---------------------------------------------------------------------- * | Level | Last Value Used | Holes | * ---------------------------------------------------------------------- - * | Module Init and Probe | 0x0123 | 0x4b,0xba,0xfa | - * | Mailbox commands | 0x1140 | 0x111a-0x111b | + * | Module Init and Probe | 0x0124 | 0x4b,0xba,0xfa | + * | Mailbox commands | 0x114c | 0x111a-0x111b | * | | | 0x112c-0x112e | * | | | 0x113a | * | Device Discovery | 0x2087 | 0x2020-0x2022 | * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 | * | | | 0x302d-0x302e | * | DPC Thread | 0x401c | 0x4002,0x4013 | - * | Async Events | 0x505f | 0x502b-0x502f | + * | Async Events | 0x506c | 0x502b-0x502f | * | | | 0x5047,0x5052 | * | Timer Routines | 0x6011 | | - * | User Space Interactions | 0x70bb | 0x7018,0x702e, | + * | User Space Interactions | 0x70bd | 0x7018,0x702e, | * | | | 0x7039,0x7045, | * | | | 0x7073-0x7075, | * | | | 0x708c, | @@ -33,7 +33,7 @@ * | | | 0x800b,0x8039 | * | AER/EEH | 0x9011 | | * | Virtual Port | 0xa007 | | - * | ISP82XX Specific | 0xb055 | 0xb024 | + * | ISP82XX Specific | 0xb080 | 0xb024 | * | MultiQ | 0xc00c | | * | Misc | 0xd010 | | * | Target Mode | 0xe06f | | diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 9926d9b61285..f82e7bbd9354 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -114,6 +114,82 @@ #define WRT_REG_WORD(addr, data) writew(data,addr) #define WRT_REG_DWORD(addr, data) writel(data,addr) +/* + * ISP83XX specific remote register addresses + */ +#define QLA83XX_LED_PORT0 0x00201320 +#define QLA83XX_LED_PORT1 0x00201328 +#define QLA83XX_IDC_DEV_STATE 0x22102384 +#define QLA83XX_IDC_MAJOR_VERSION 0x22102380 +#define QLA83XX_IDC_MINOR_VERSION 0x22102398 +#define QLA83XX_IDC_DRV_PRESENCE 0x22102388 +#define QLA83XX_IDC_DRIVER_ACK 0x2210238c +#define QLA83XX_IDC_CONTROL 0x22102390 +#define QLA83XX_IDC_AUDIT 0x22102394 +#define QLA83XX_IDC_LOCK_RECOVERY 0x2210239c +#define QLA83XX_DRIVER_LOCKID 0x22102104 +#define QLA83XX_DRIVER_LOCK 0x8111c028 +#define QLA83XX_DRIVER_UNLOCK 0x8111c02c +#define QLA83XX_FLASH_LOCKID 0x22102100 +#define QLA83XX_FLASH_LOCK 0x8111c010 +#define QLA83XX_FLASH_UNLOCK 0x8111c014 +#define QLA83XX_DEV_PARTINFO1 0x221023e0 +#define QLA83XX_DEV_PARTINFO2 0x221023e4 +#define QLA83XX_FW_HEARTBEAT 0x221020b0 +#define QLA83XX_PEG_HALT_STATUS1 0x221020a8 +#define QLA83XX_PEG_HALT_STATUS2 0x221020ac + +/* 83XX: Macros defining 8200 AEN Reason codes */ +#define IDC_DEVICE_STATE_CHANGE BIT_0 +#define IDC_PEG_HALT_STATUS_CHANGE BIT_1 +#define IDC_NIC_FW_REPORTED_FAILURE BIT_2 +#define IDC_HEARTBEAT_FAILURE BIT_3 + +/* 83XX: Macros defining 8200 AEN Error-levels */ +#define ERR_LEVEL_NON_FATAL 0x1 +#define ERR_LEVEL_RECOVERABLE_FATAL 0x2 +#define ERR_LEVEL_UNRECOVERABLE_FATAL 0x4 + +/* 83XX: Macros for IDC Version */ +#define QLA83XX_SUPP_IDC_MAJOR_VERSION 0x01 +#define QLA83XX_SUPP_IDC_MINOR_VERSION 0x0 + +/* 83XX: Macros for scheduling dpc tasks */ +#define QLA83XX_NIC_CORE_RESET 0x1 +#define QLA83XX_IDC_STATE_HANDLER 0x2 +#define QLA83XX_NIC_CORE_UNRECOVERABLE 0x3 + +/* 83XX: Macros for defining IDC-Control bits */ +#define QLA83XX_IDC_RESET_DISABLED BIT_0 +#define QLA83XX_IDC_GRACEFUL_RESET BIT_1 + +/* 83XX: Macros for different timeouts */ +#define QLA83XX_IDC_INITIALIZATION_TIMEOUT 30 +#define QLA83XX_IDC_RESET_ACK_TIMEOUT 10 +#define QLA83XX_MAX_LOCK_RECOVERY_WAIT (2 * HZ) + +/* 83XX: Macros for defining class in DEV-Partition Info register */ +#define QLA83XX_CLASS_TYPE_NONE 0x0 +#define QLA83XX_CLASS_TYPE_NIC 0x1 +#define QLA83XX_CLASS_TYPE_FCOE 0x2 +#define QLA83XX_CLASS_TYPE_ISCSI 0x3 + +/* 83XX: Macros for IDC Lock-Recovery stages */ +#define IDC_LOCK_RECOVERY_STAGE1 0x1 /* Stage1: Intent for + * lock-recovery + */ +#define IDC_LOCK_RECOVERY_STAGE2 0x2 /* Stage2: Perform lock-recovery */ + +/* 83XX: Macros for IDC Audit type */ +#define IDC_AUDIT_TIMESTAMP 0x0 /* IDC-AUDIT: Record timestamp of + * dev-state change to NEED-RESET + * or NEED-QUIESCENT + */ +#define IDC_AUDIT_COMPLETION 0x1 /* IDC-AUDIT: Record duration of + * reset-recovery completion is + * second + */ + /* * The ISP2312 v2 chip cannot access the FLASH/GPIO registers via MMIO in an * 133Mhz slot. @@ -596,6 +672,9 @@ typedef struct { #define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */ #define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */ +/* 83XX FCoE specific */ +#define MBA_IDC_AEN 0x8200 /* FCoE: NIC Core state change AEN */ + /* ISP mailbox loopback echo diagnostic error code */ #define MBS_LB_RESET 0x17 /* @@ -2523,11 +2602,12 @@ struct qla_hw_data { uint32_t disable_msix_handshake :1; uint32_t fcp_prio_enabled :1; uint32_t isp82xx_fw_hung:1; + uint32_t nic_core_hung:1; uint32_t quiesce_owner:1; uint32_t thermal_supported:1; - uint32_t isp82xx_reset_hdlr_active:1; - uint32_t isp82xx_reset_owner:1; + uint32_t nic_core_reset_hdlr_active:1; + uint32_t nic_core_reset_owner:1; uint32_t isp82xx_no_md_cap:1; uint32_t host_shutting_down:1; /* 30 bits */ @@ -2912,8 +2992,8 @@ struct qla_hw_data { unsigned long mn_win_crb; unsigned long ms_win_crb; int qdr_sn_window; - uint32_t nx_dev_init_timeout; - uint32_t nx_reset_timeout; + uint32_t fcoe_dev_init_timeout; + uint32_t fcoe_reset_timeout; rwlock_t hw_lock; uint16_t portnum; /* port number */ int link_width; @@ -2935,6 +3015,19 @@ struct qla_hw_data { uint32_t md_dump_size; void *loop_id_map; + + /* QLA83XX IDC specific fields */ + uint32_t idc_audit_ts; + + /* DPC low-priority workqueue */ + struct workqueue_struct *dpc_lp_wq; + struct work_struct idc_aen; + /* DPC high-priority workqueue */ + struct workqueue_struct *dpc_hp_wq; + struct work_struct nic_core_reset; + struct work_struct idc_state_handler; + struct work_struct nic_core_unrecoverable; + struct qlt_hw_data tgt; }; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index a678ed5d6884..84543fe487cd 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1540,7 +1540,10 @@ struct access_chip_rsp_84xx { /* * ISP83xx mailbox commands */ -#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ +#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ +#define MBC_READ_REMOTE_REG 0x0009 /* Read remote register */ +#define MBC_RESTART_NIC_FIRMWARE 0x003d /* Restart NIC firmware */ +#define MBC_SET_ACCESS_CONTROL 0x003e /* Access control command */ /* Flash access control option field bit definitions */ #define FAC_OPT_FORCE_SEMAPHORE BIT_15 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index d7588adc768b..98c187da2c91 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -76,6 +76,13 @@ extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *); extern fc_port_t * qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t ); + +extern int __qla83xx_set_idc_control(scsi_qla_host_t *, uint32_t); +extern int __qla83xx_get_idc_control(scsi_qla_host_t *, uint32_t *); +extern void qla83xx_idc_audit(scsi_qla_host_t *, int); +extern int qla83xx_nic_core_reset(scsi_qla_host_t *); +extern void qla83xx_reset_ownership(scsi_qla_host_t *); + /* * Global Data in qla_os.c source file. */ @@ -133,6 +140,20 @@ extern void qla2x00_relogin(struct scsi_qla_host *); extern void qla2x00_do_work(struct scsi_qla_host *); extern void qla2x00_free_fcports(struct scsi_qla_host *); +extern void qla83xx_schedule_work(scsi_qla_host_t *, int); +extern void qla83xx_service_idc_aen(struct work_struct *); +extern void qla83xx_nic_core_unrecoverable_work(struct work_struct *); +extern void qla83xx_idc_state_handler_work(struct work_struct *); +extern void qla83xx_nic_core_reset_work(struct work_struct *); + +extern void qla83xx_idc_lock(scsi_qla_host_t *, uint16_t); +extern void qla83xx_idc_unlock(scsi_qla_host_t *, uint16_t); +extern int qla83xx_idc_state_handler(scsi_qla_host_t *); +extern int qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); + /* * Global Functions in qla_mid.c source file. */ @@ -421,7 +442,11 @@ extern void qla24xx_beacon_blink(struct scsi_qla_host *); extern void qla83xx_beacon_blink(struct scsi_qla_host *); extern int qla82xx_beacon_on(struct scsi_qla_host *); extern int qla82xx_beacon_off(struct scsi_qla_host *); -extern int qla83xx_write_remote_reg(struct scsi_qla_host *, uint32_t, uint32_t); +extern int qla83xx_wr_reg(scsi_qla_host_t *, uint32_t, uint32_t); +extern int qla83xx_rd_reg(scsi_qla_host_t *, uint32_t, uint32_t *); +extern int qla83xx_restart_nic_firmware(scsi_qla_host_t *); +extern int qla83xx_access_control(scsi_qla_host_t *, uint16_t, uint32_t, + uint32_t, uint16_t *); extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); @@ -582,6 +607,7 @@ extern uint32_t qla82xx_wait_for_state_change(scsi_qla_host_t *, uint32_t); extern int qla82xx_idc_lock(struct qla_hw_data *); extern void qla82xx_idc_unlock(struct qla_hw_data *); extern int qla82xx_device_state_handler(scsi_qla_host_t *); +extern void qla8xxx_dev_failed_handler(scsi_qla_host_t *); extern void qla82xx_clear_qsnt_ready(scsi_qla_host_t *); extern void qla2x00_set_model_info(scsi_qla_host_t *, uint8_t *, diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 4aef7f0f5a91..71310d3c6fd4 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -429,6 +429,71 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, /* QLogic ISP2x00 Hardware Support Functions. */ /****************************************************************************/ +int +qla83xx_nic_core_fw_load(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t idc_major_ver, idc_minor_ver; + + qla83xx_idc_lock(vha, 0); + + /* SV: TODO: Assign initialization timeout from + * flash-info / other param + */ + ha->fcoe_dev_init_timeout = QLA83XX_IDC_INITIALIZATION_TIMEOUT; + ha->fcoe_reset_timeout = QLA83XX_IDC_RESET_ACK_TIMEOUT; + + /* Set our fcoe function presence */ + if (__qla83xx_set_drv_presence(vha) != QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, vha, 0xb077, + "Error while setting DRV-Presence.\n"); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + + /* Decide the reset ownership */ + qla83xx_reset_ownership(vha); + + /* + * On first protocol driver load: + * Init-Owner: Set IDC-Major-Version and Clear IDC-Lock-Recovery + * register. + * Others: Check compatibility with current IDC Major version. + */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MAJOR_VERSION, &idc_major_ver); + if (ha->flags.nic_core_reset_owner) { + /* Set IDC Major version */ + idc_major_ver = QLA83XX_SUPP_IDC_MAJOR_VERSION; + qla83xx_wr_reg(vha, QLA83XX_IDC_MAJOR_VERSION, idc_major_ver); + + /* Clearing IDC-Lock-Recovery register */ + qla83xx_wr_reg(vha, QLA83XX_IDC_LOCK_RECOVERY, 0); + } else if (idc_major_ver != QLA83XX_SUPP_IDC_MAJOR_VERSION) { + /* + * Clear further IDC participation if we are not compatible with + * the current IDC Major Version. + */ + ql_log(ql_log_warn, vha, 0xb07d, + "Failing load, idc_major_ver=%d, expected_major_ver=%d.\n", + idc_major_ver, QLA83XX_SUPP_IDC_MAJOR_VERSION); + __qla83xx_clear_drv_presence(vha); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + /* Each function sets its supported Minor version. */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MINOR_VERSION, &idc_minor_ver); + idc_minor_ver |= (QLA83XX_SUPP_IDC_MINOR_VERSION << (ha->portnum * 2)); + qla83xx_wr_reg(vha, QLA83XX_IDC_MINOR_VERSION, idc_minor_ver); + + rval = qla83xx_idc_state_handler(vha); + +exit: + qla83xx_idc_unlock(vha, 0); + + return rval; +} + /* * qla2x00_initialize_adapter * Initialize board. @@ -537,6 +602,14 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) } } + /* Load the NIC Core f/w if we are the first protocol driver. */ + if (IS_QLA8031(ha)) { + rval = qla83xx_nic_core_fw_load(vha); + if (rval) + ql_log(ql_log_warn, vha, 0x0124, + "Error in initializing NIC Core f/w.\n"); + } + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) qla24xx_read_fcp_prio_cfg(vha); @@ -3736,6 +3809,307 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) spin_unlock_irqrestore(&ha->vport_slock, flags); } +/* Assumes idc_lock always held on entry */ +void +qla83xx_reset_ownership(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence, drv_presence_mask; + uint32_t dev_part_info1, dev_part_info2, class_type; + uint32_t class_type_mask = 0x3; + uint16_t fcoe_other_function = 0xffff, i; + + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO1, &dev_part_info1); + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO2, &dev_part_info2); + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info1 >> (i * 4)) & class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + (i != ha->portnum)) { + fcoe_other_function = i; + break; + } + } + if (fcoe_other_function == 0xffff) { + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info2 >> (i * 4)) & + class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + ((i + 8) != ha->portnum)) { + fcoe_other_function = i + 8; + break; + } + } + } + /* + * Prepare drv-presence mask based on fcoe functions present. + * However consider only valid physical fcoe function numbers (0-15). + */ + drv_presence_mask = ~((1 << (ha->portnum)) | + ((fcoe_other_function == 0xffff) ? + 0 : (1 << (fcoe_other_function)))); + + /* We are the reset owner iff: + * - No other protocol drivers present. + * - This is the lowest among fcoe functions. */ + if (!(drv_presence & drv_presence_mask) && + (ha->portnum < fcoe_other_function)) { + ql_dbg(ql_dbg_p3p, vha, 0xb07f, + "This host is Reset owner.\n"); + ha->flags.nic_core_reset_owner = 1; + } +} + +int +__qla83xx_set_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack |= (1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +int +qla83xx_set_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_drv_ack(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_clear_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack &= ~(1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +int +qla83xx_clear_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_clear_drv_ack(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +const char * +qla83xx_dev_state_to_string(uint32_t dev_state) +{ + switch (dev_state) { + case QLA8XXX_DEV_COLD: + return "COLD/RE-INIT"; + case QLA8XXX_DEV_INITIALIZING: + return "INITIALIZING"; + case QLA8XXX_DEV_READY: + return "READY"; + case QLA8XXX_DEV_NEED_RESET: + return "NEED RESET"; + case QLA8XXX_DEV_NEED_QUIESCENT: + return "NEED QUIESCENT"; + case QLA8XXX_DEV_FAILED: + return "FAILED"; + case QLA8XXX_DEV_QUIESCENT: + return "QUIESCENT"; + default: + return "Unknown"; + } +} + +/* Assumes idc-lock always held on entry */ +void +qla83xx_idc_audit(scsi_qla_host_t *vha, int audit_type) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_audit_reg = 0, duration_secs = 0; + + switch (audit_type) { + case IDC_AUDIT_TIMESTAMP: + ha->idc_audit_ts = (jiffies_to_msecs(jiffies) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_TIMESTAMP << 7) | (ha->idc_audit_ts << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + case IDC_AUDIT_COMPLETION: + duration_secs = ((jiffies_to_msecs(jiffies) - + jiffies_to_msecs(ha->idc_audit_ts)) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_COMPLETION << 7) | (duration_secs << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + default: + ql_log(ql_log_warn, vha, 0xb078, + "Invalid audit type specified.\n"); + break; + } +} + +/* Assumes idc_lock always held on entry */ +int +qla83xx_initiating_reset(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_control, dev_state; + + __qla83xx_get_idc_control(vha, &idc_control); + if ((idc_control & QLA83XX_IDC_RESET_DISABLED)) { + ql_log(ql_log_info, vha, 0xb080, + "NIC Core reset has been disabled. idc-control=0x%x\n", + idc_control); + return QLA_FUNCTION_FAILED; + } + + /* Set NEED-RESET iff in READY state and we are the reset-owner */ + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + if (ha->flags.nic_core_reset_owner && dev_state == QLA8XXX_DEV_READY) { + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + ql_log(ql_log_info, vha, 0xb056, "HW State: NEED RESET.\n"); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + } else { + const char *state = qla83xx_dev_state_to_string(dev_state); + ql_log(ql_log_info, vha, 0xb057, "HW State: %s.\n", state); + + /* SV: XXX: Is timeout required here? */ + /* Wait for IDC state change READY -> NEED_RESET */ + while (dev_state == QLA8XXX_DEV_READY) { + qla83xx_idc_unlock(vha, 0); + msleep(200); + qla83xx_idc_lock(vha, 0); + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + } + } + + /* Send IDC ack by writing to drv-ack register */ + __qla83xx_set_drv_ack(vha); + + return QLA_SUCCESS; +} + +int +__qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control) +{ + return qla83xx_wr_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +int +qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control) +{ + return qla83xx_rd_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +int +qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_get_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +qla83xx_check_driver_presence(scsi_qla_host_t *vha) +{ + uint32_t drv_presence = 0; + struct qla_hw_data *ha = vha->hw; + + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (drv_presence & (1 << ha->portnum)) + return QLA_SUCCESS; + else + return QLA_TEST_FAILED; +} + +int +qla83xx_nic_core_reset(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb058, + "Entered %s().\n", __func__); + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_log(ql_log_warn, vha, 0xb059, + "Device in unrecoverable FAILED state.\n"); + return QLA_FUNCTION_FAILED; + } + + qla83xx_idc_lock(vha, 0); + + if (qla83xx_check_driver_presence(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb05a, + "Function=0x%x has been removed from IDC participation.\n", + ha->portnum); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + + qla83xx_reset_ownership(vha); + + rval = qla83xx_initiating_reset(vha); + + /* + * Perform reset if we are the reset-owner, + * else wait till IDC state changes to READY/FAILED. + */ + if (rval == QLA_SUCCESS) { + rval = qla83xx_idc_state_handler(vha); + + if (rval == QLA_SUCCESS) + ha->flags.nic_core_hung = 0; + __qla83xx_clear_drv_ack(vha); + } + +exit: + qla83xx_idc_unlock(vha, 0); + + ql_dbg(ql_dbg_p3p, vha, 0xb05b, "Exiting %s.\n", __func__); + + return rval; +} + /* * qla82xx_quiescent_state_cleanup * Description: This function will block the new I/Os @@ -3871,6 +4245,14 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) struct req_que *req = ha->req_q_map[0]; unsigned long flags; + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05c, + "Clearing fcoe driver presence.\n"); + if (qla83xx_clear_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb073, + "Erro while clearing DRV-Presence.\n"); + } + if (vha->flags.online) { qla2x00_abort_isp_cleanup(vha); @@ -3982,6 +4364,13 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) } spin_unlock_irqrestore(&ha->vport_slock, flags); + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05d, + "Setting back fcoe driver presence.\n"); + if (qla83xx_set_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb074, + "Error while setting DRV-Presence.\n"); + } } else { ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n", __func__); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 2ea848c01199..681a9f897fa3 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -332,6 +332,166 @@ qla2x00_get_link_speed_str(struct qla_hw_data *ha) return link_speed; } +void +qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb) +{ + struct qla_hw_data *ha = vha->hw; + + /* + * 8200 AEN Interpretation: + * mb[0] = AEN code + * mb[1] = AEN Reason code + * mb[2] = LSW of Peg-Halt Status-1 Register + * mb[6] = MSW of Peg-Halt Status-1 Register + * mb[3] = LSW of Peg-Halt Status-2 register + * mb[7] = MSW of Peg-Halt Status-2 register + * mb[4] = IDC Device-State Register value + * mb[5] = IDC Driver-Presence Register value + */ + ql_dbg(ql_dbg_async, vha, 0x506b, "AEN Code: mb[0] = 0x%x AEN reason: " + "mb[1] = 0x%x PH-status1: mb[2] = 0x%x PH-status1: mb[6] = 0x%x.\n", + mb[0], mb[1], mb[2], mb[6]); + ql_dbg(ql_dbg_async, vha, 0x506c, "PH-status2: mb[3] = 0x%x " + "PH-status2: mb[7] = 0x%x Device-State: mb[4] = 0x%x " + "Drv-Presence: mb[5] = 0x%x.\n", mb[3], mb[7], mb[4], mb[5]); + + if (mb[1] & (IDC_PEG_HALT_STATUS_CHANGE | IDC_NIC_FW_REPORTED_FAILURE | + IDC_HEARTBEAT_FAILURE)) { + ha->flags.nic_core_hung = 1; + ql_log(ql_log_warn, vha, 0x5060, + "83XX: F/W Error Reported: Check if reset required.\n"); + + if (mb[1] & IDC_PEG_HALT_STATUS_CHANGE) { + uint32_t protocol_engine_id, fw_err_code, err_level; + + /* + * IDC_PEG_HALT_STATUS_CHANGE interpretation: + * - PEG-Halt Status-1 Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = protocol-engine ID + * Bits 8-28 = f/w error code + * Bits 29-31 = Error-level + * Error-level 0x1 = Non-Fatal error + * Error-level 0x2 = Recoverable Fatal error + * Error-level 0x4 = UnRecoverable Fatal error + * - PEG-Halt Status-2 Register: + * (LSW = mb[3], MSW = mb[7]) + */ + protocol_engine_id = (mb[2] & 0xff); + fw_err_code = (((mb[2] & 0xff00) >> 8) | + ((mb[6] & 0x1fff) << 8)); + err_level = ((mb[6] & 0xe000) >> 13); + ql_log(ql_log_warn, vha, 0x5061, "PegHalt Status-1 " + "Register: protocol_engine_id=0x%x " + "fw_err_code=0x%x err_level=0x%x.\n", + protocol_engine_id, fw_err_code, err_level); + ql_log(ql_log_warn, vha, 0x5062, "PegHalt Status-2 " + "Register: 0x%x%x.\n", mb[7], mb[3]); + if (err_level == ERR_LEVEL_NON_FATAL) { + ql_log(ql_log_warn, vha, 0x5063, + "Not a fatal error, f/w has recovered " + "iteself.\n"); + } else if (err_level == ERR_LEVEL_RECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5064, + "Recoverable Fatal error: Chip reset " + "required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_RESET); + } else if (err_level == ERR_LEVEL_UNRECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5065, + "Unrecoverable Fatal error: Set FAILED " + "state, reboot required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_UNRECOVERABLE); + } + } + + if (mb[1] & IDC_NIC_FW_REPORTED_FAILURE) { + uint16_t peg_fw_state, nw_interface_link_up; + uint16_t nw_interface_signal_detect, sfp_status; + uint16_t htbt_counter, htbt_monitor_enable; + uint16_t sfp_additonal_info, sfp_multirate; + uint16_t sfp_tx_fault, link_speed, dcbx_status; + + /* + * IDC_NIC_FW_REPORTED_FAILURE interpretation: + * - PEG-to-FC Status Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = Peg-Firmware state + * Bit 8 = N/W Interface Link-up + * Bit 9 = N/W Interface signal detected + * Bits 10-11 = SFP Status + * SFP Status 0x0 = SFP+ transceiver not expected + * SFP Status 0x1 = SFP+ transceiver not present + * SFP Status 0x2 = SFP+ transceiver invalid + * SFP Status 0x3 = SFP+ transceiver present and + * valid + * Bits 12-14 = Heartbeat Counter + * Bit 15 = Heartbeat Monitor Enable + * Bits 16-17 = SFP Additional Info + * SFP info 0x0 = Unregocnized transceiver for + * Ethernet + * SFP info 0x1 = SFP+ brand validation failed + * SFP info 0x2 = SFP+ speed validation failed + * SFP info 0x3 = SFP+ access error + * Bit 18 = SFP Multirate + * Bit 19 = SFP Tx Fault + * Bits 20-22 = Link Speed + * Bits 23-27 = Reserved + * Bits 28-30 = DCBX Status + * DCBX Status 0x0 = DCBX Disabled + * DCBX Status 0x1 = DCBX Enabled + * DCBX Status 0x2 = DCBX Exchange error + * Bit 31 = Reserved + */ + peg_fw_state = (mb[2] & 0x00ff); + nw_interface_link_up = ((mb[2] & 0x0100) >> 8); + nw_interface_signal_detect = ((mb[2] & 0x0200) >> 9); + sfp_status = ((mb[2] & 0x0c00) >> 10); + htbt_counter = ((mb[2] & 0x7000) >> 12); + htbt_monitor_enable = ((mb[2] & 0x8000) >> 15); + sfp_additonal_info = (mb[6] & 0x0003); + sfp_multirate = ((mb[6] & 0x0004) >> 2); + sfp_tx_fault = ((mb[6] & 0x0008) >> 3); + link_speed = ((mb[6] & 0x0070) >> 4); + dcbx_status = ((mb[6] & 0x7000) >> 12); + + ql_log(ql_log_warn, vha, 0x5066, + "Peg-to-Fc Status Register:\n" + "peg_fw_state=0x%x, nw_interface_link_up=0x%x, " + "nw_interface_signal_detect=0x%x" + "\nsfp_statis=0x%x.\n ", peg_fw_state, + nw_interface_link_up, nw_interface_signal_detect, + sfp_status); + ql_log(ql_log_warn, vha, 0x5067, + "htbt_counter=0x%x, htbt_monitor_enable=0x%x, " + "sfp_additonal_info=0x%x, sfp_multirate=0x%x.\n ", + htbt_counter, htbt_monitor_enable, + sfp_additonal_info, sfp_multirate); + ql_log(ql_log_warn, vha, 0x5068, + "sfp_tx_fault=0x%x, link_state=0x%x, " + "dcbx_status=0x%x.\n", sfp_tx_fault, link_speed, + dcbx_status); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + + if (mb[1] & IDC_HEARTBEAT_FAILURE) { + ql_log(ql_log_warn, vha, 0x5069, + "Heartbeat Failure encountered, chip reset " + "required.\n"); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + } + + if (mb[1] & IDC_DEVICE_STATE_CHANGE) { + ql_log(ql_log_info, vha, 0x506a, + "IDC Device-State changed = 0x%x.\n", mb[4]); + qla83xx_schedule_work(vha, MBA_IDC_AEN); + } +} + /** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context @@ -825,8 +985,18 @@ skip_rio: case MBA_IDC_COMPLETE: case MBA_IDC_NOTIFY: case MBA_IDC_TIME_EXT: - qla81xx_idc_event(vha, mb[0], mb[1]); + if (IS_QLA81XX(vha->hw)) + qla81xx_idc_event(vha, mb[0], mb[1]); + break; + + case MBA_IDC_AEN: + mb[4] = RD_REG_WORD(®24->mailbox4); + mb[5] = RD_REG_WORD(®24->mailbox5); + mb[6] = RD_REG_WORD(®24->mailbox6); + mb[7] = RD_REG_WORD(®24->mailbox7); + qla83xx_handle_8200_aen(vha, mb); break; + default: ql_dbg(ql_dbg_async, vha, 0x5057, "Unknown AEN:%04x %04x %04x %04x\n", @@ -2301,7 +2471,7 @@ qla24xx_intr_handler(int irq, void *dev_id) unsigned long iter; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; struct rsp_que *rsp; unsigned long flags; @@ -2457,7 +2627,7 @@ qla24xx_msix_default(int irq, void *dev_id) int status; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; unsigned long flags; rsp = (struct rsp_que *) dev_id; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index f8a1a64c0ca7..1587b64b4bc3 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -75,7 +75,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) return QLA_FUNCTION_TIMEOUT; } - if (ha->flags.isp82xx_fw_hung) { + if (IS_QLA82XX(ha) && ha->flags.isp82xx_fw_hung) { /* Setting Link-Down error */ mcp->mb[0] = MBS_LINK_DOWN_ERROR; ql_log(ql_log_warn, vha, 0x1004, @@ -232,7 +232,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) ha->flags.mbox_int = 0; clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - if (ha->flags.isp82xx_fw_hung) { + if ((IS_QLA82XX(ha) && ha->flags.isp82xx_fw_hung)) { ha->flags.mbox_busy = 0; /* Setting Link-Down error */ mcp->mb[0] = MBS_LINK_DOWN_ERROR; @@ -4741,7 +4741,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable) } int -qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) +qla83xx_wr_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) { int rval; struct qla_hw_data *ha = vha->hw; @@ -4814,3 +4814,139 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport) return rval; } +int +qla83xx_rd_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + unsigned long retry_max_time = jiffies + (2 * HZ); + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x114b, "Entered %s.\n", __func__); + +retry_rd_reg: + mcp->mb[0] = MBC_READ_REMOTE_REG; + mcp->mb[1] = LSW(reg); + mcp->mb[2] = MSW(reg); + mcp->out_mb = MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x114c, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + *data = (mcp->mb[3] | (mcp->mb[4] << 16)); + if (*data == QLA8XXX_BAD_VALUE) { + /* + * During soft-reset CAMRAM register reads might + * return 0xbad0bad0. So retry for MAX of 2 sec + * while reading camram registers. + */ + if (time_after(jiffies, retry_max_time)) { + ql_dbg(ql_dbg_mbx, vha, 0x1141, + "Failure to read CAMRAM register. " + "data=0x%x.\n", *data); + return QLA_FUNCTION_FAILED; + } + msleep(100); + goto retry_rd_reg; + } + ql_dbg(ql_dbg_mbx, vha, 0x1142, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_restart_nic_firmware(scsi_qla_host_t *vha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_RESTART_NIC_FIRMWARE; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1144, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + ha->isp_ops->fw_dump(vha, 0); + } else { + ql_dbg(ql_dbg_mbx, vha, 0x1145, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_access_control(scsi_qla_host_t *vha, uint16_t options, + uint32_t start_addr, uint32_t end_addr, uint16_t *sector_size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint8_t subcode = (uint8_t)options; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA8031(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1146, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_ACCESS_CONTROL; + mcp->mb[1] = options; + mcp->out_mb = MBX_1|MBX_0; + if (subcode & BIT_2) { + mcp->mb[2] = LSW(start_addr); + mcp->mb[3] = MSW(start_addr); + mcp->mb[4] = LSW(end_addr); + mcp->mb[5] = MSW(end_addr); + mcp->out_mb |= MBX_5|MBX_4|MBX_3|MBX_2; + } + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (!(subcode & (BIT_2 | BIT_5))) + mcp->in_mb |= MBX_4|MBX_3; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1147, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[4]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], + mcp->mb[4]); + ha->isp_ops->fw_dump(vha, 0); + } else { + if (subcode & BIT_5) + *sector_size = mcp->mb[1]; + else if (subcode & (BIT_6 | BIT_7)) { + ql_dbg(ql_dbg_mbx, vha, 0x1148, + "Driver-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } else if (subcode & (BIT_3 | BIT_4)) { + ql_dbg(ql_dbg_mbx, vha, 0x1149, + "Flash-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } + ql_dbg(ql_dbg_mbx, vha, 0x114a, "Done %s.\n", __func__); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 642aea04faa7..75cdf200f453 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -2355,7 +2355,7 @@ qla82xx_need_reset(struct qla_hw_data *ha) uint32_t drv_state; int rval; - if (ha->flags.isp82xx_reset_owner) + if (ha->flags.nic_core_reset_owner) return 1; else { drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); @@ -2864,7 +2864,7 @@ qla82xx_device_bootstrap(scsi_qla_host_t *vha) timeout = msleep_interruptible(200); if (timeout) { qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_FAILED); + QLA8XXX_DEV_FAILED); return QLA_FUNCTION_FAILED; } @@ -2895,7 +2895,7 @@ dev_initialize: /* set to DEV_INITIALIZING */ ql_log(ql_log_info, vha, 0x009e, "HW State: INITIALIZING.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_INITIALIZING); /* Driver that sets device state to initializating sets IDC version */ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); @@ -2908,14 +2908,14 @@ dev_initialize: ql_log(ql_log_fatal, vha, 0x00ad, "HW State: FAILED.\n"); qla82xx_clear_drv_active(ha); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_FAILED); return rval; } dev_ready: ql_log(ql_log_info, vha, 0x00ae, "HW State: READY.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_READY); return QLA_SUCCESS; } @@ -2964,7 +2964,7 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME, drv_active, drv_state); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_READY); + QLA8XXX_DEV_READY); ql_log(ql_log_info, vha, 0xb025, "HW State: DEV_READY.\n"); qla82xx_idc_unlock(ha); @@ -2985,10 +2985,10 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) } 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) { + if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { ql_log(ql_log_info, vha, 0xb026, "HW State: DEV_QUIESCENT.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_QUIESCENT); } } @@ -3018,8 +3018,8 @@ qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state) return dev_state; } -static void -qla82xx_dev_failed_handler(scsi_qla_host_t *vha) +void +qla8xxx_dev_failed_handler(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; @@ -3067,7 +3067,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) } drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); - if (!ha->flags.isp82xx_reset_owner) { + if (!ha->flags.nic_core_reset_owner) { ql_dbg(ql_dbg_p3p, vha, 0xb028, "reset_acknowledged by 0x%x\n", ha->portnum); qla82xx_set_rst_ready(ha); @@ -3079,7 +3079,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) } /* wait for 10 seconds for reset ack from all functions */ - reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); + reset_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); @@ -3091,7 +3091,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) drv_state, drv_active, dev_state, active_mask); while (drv_state != drv_active && - dev_state != QLA82XX_DEV_INITIALIZING) { + dev_state != QLA8XXX_DEV_INITIALIZING) { if (time_after_eq(jiffies, reset_timeout)) { ql_log(ql_log_warn, vha, 0x00b5, "Reset timeout.\n"); @@ -3102,7 +3102,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); - if (ha->flags.isp82xx_reset_owner) + if (ha->flags.nic_core_reset_owner) drv_active &= active_mask; dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); } @@ -3118,11 +3118,11 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); /* Force to DEV_COLD unless someone else is starting a reset */ - if (dev_state != QLA82XX_DEV_INITIALIZING && - dev_state != QLA82XX_DEV_COLD) { + if (dev_state != QLA8XXX_DEV_INITIALIZING && + dev_state != QLA8XXX_DEV_COLD) { ql_log(ql_log_info, vha, 0x00b7, "HW State: COLD/RE-INIT.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_COLD); qla82xx_set_rst_ready(ha); if (ql2xmdenable) { if (qla82xx_md_collect(vha)) @@ -3240,7 +3240,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); /* wait for 30 seconds for device to go ready */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); while (1) { @@ -3264,18 +3264,18 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) } switch (dev_state) { - case QLA82XX_DEV_READY: - ha->flags.isp82xx_reset_owner = 0; + case QLA8XXX_DEV_READY: + ha->flags.nic_core_reset_owner = 0; goto rel_lock; - case QLA82XX_DEV_COLD: + case QLA8XXX_DEV_COLD: rval = qla82xx_device_bootstrap(vha); break; - case QLA82XX_DEV_INITIALIZING: + case QLA8XXX_DEV_INITIALIZING: qla82xx_idc_unlock(ha); msleep(1000); qla82xx_idc_lock(ha); break; - case QLA82XX_DEV_NEED_RESET: + case QLA8XXX_DEV_NEED_RESET: if (!ql2xdontresethba) qla82xx_need_reset_handler(vha); else { @@ -3284,15 +3284,15 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); } dev_init_timeout = jiffies + - (ha->nx_dev_init_timeout * HZ); + (ha->fcoe_dev_init_timeout * HZ); break; - case QLA82XX_DEV_NEED_QUIESCENT: + case QLA8XXX_DEV_NEED_QUIESCENT: qla82xx_need_qsnt_handler(vha); /* Reset timeout value after quiescence handler */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_QUIESCENT: + case QLA8XXX_DEV_QUIESCENT: /* Owner will exit and other will wait for the state * to get changed */ @@ -3304,11 +3304,11 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); /* Reset timeout value after quiescence handler */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_FAILED: - qla82xx_dev_failed_handler(vha); + case QLA8XXX_DEV_FAILED: + qla8xxx_dev_failed_handler(vha); rval = QLA_FUNCTION_FAILED; goto exit; default: @@ -3368,23 +3368,23 @@ void qla82xx_watchdog(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; /* don't poll if reset is going on */ - if (!ha->flags.isp82xx_reset_hdlr_active) { + if (!ha->flags.nic_core_reset_hdlr_active) { dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); if (qla82xx_check_temp(vha)) { set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); ha->flags.isp82xx_fw_hung = 1; qla82xx_clear_pending_mbx(vha); - } else if (dev_state == QLA82XX_DEV_NEED_RESET && + } else if (dev_state == QLA8XXX_DEV_NEED_RESET && !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { ql_log(ql_log_warn, vha, 0x6001, "Adapter reset needed.\n"); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT && + } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT && !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { ql_log(ql_log_warn, vha, 0x6002, "Quiescent needed.\n"); set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); - } else if (dev_state == QLA82XX_DEV_FAILED && + } else if (dev_state == QLA8XXX_DEV_FAILED && !test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) && vha->flags.online == 1) { ql_log(ql_log_warn, vha, 0xb055, @@ -3453,12 +3453,12 @@ qla82xx_set_reset_owner(scsi_qla_host_t *vha) uint32_t dev_state; dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - if (dev_state == QLA82XX_DEV_READY) { + if (dev_state == QLA8XXX_DEV_READY) { ql_log(ql_log_info, vha, 0xb02f, "HW State: NEED RESET\n"); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_NEED_RESET); - ha->flags.isp82xx_reset_owner = 1; + QLA8XXX_DEV_NEED_RESET); + ha->flags.nic_core_reset_owner = 1; ql_dbg(ql_dbg_p3p, vha, 0xb030, "reset_owner is 0x%x\n", ha->portnum); } else @@ -3489,7 +3489,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) "Device in failed state, exiting.\n"); return QLA_SUCCESS; } - ha->flags.isp82xx_reset_hdlr_active = 1; + ha->flags.nic_core_reset_hdlr_active = 1; qla82xx_idc_lock(ha); qla82xx_set_reset_owner(vha); @@ -3503,7 +3503,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) if (rval == QLA_SUCCESS) { ha->flags.isp82xx_fw_hung = 0; - ha->flags.isp82xx_reset_hdlr_active = 0; + ha->flags.nic_core_reset_hdlr_active = 0; qla82xx_restart_isp(vha); } diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h index 6eb210e3cc63..d0aa16ccf49a 100644 --- a/drivers/scsi/qla2xxx/qla_nx.h +++ b/drivers/scsi/qla2xxx/qla_nx.h @@ -542,14 +542,15 @@ #define QLA82XX_CRB_DRV_IDC_VERSION (QLA82XX_CAM_RAM(0x174)) /* Every driver should use these Device State */ -#define QLA82XX_DEV_COLD 1 -#define QLA82XX_DEV_INITIALIZING 2 -#define QLA82XX_DEV_READY 3 -#define QLA82XX_DEV_NEED_RESET 4 -#define QLA82XX_DEV_NEED_QUIESCENT 5 -#define QLA82XX_DEV_FAILED 6 -#define QLA82XX_DEV_QUIESCENT 7 +#define QLA8XXX_DEV_COLD 1 +#define QLA8XXX_DEV_INITIALIZING 2 +#define QLA8XXX_DEV_READY 3 +#define QLA8XXX_DEV_NEED_RESET 4 +#define QLA8XXX_DEV_NEED_QUIESCENT 5 +#define QLA8XXX_DEV_FAILED 6 +#define QLA8XXX_DEV_QUIESCENT 7 #define MAX_STATES 8 /* Increment if new state added */ +#define QLA8XXX_BAD_VALUE 0xbad0bad0 #define QLA82XX_IDC_VERSION 1 #define QLA82XX_ROM_DEV_INIT_TIMEOUT 30 diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d96adb73a1fe..8862d5b65a77 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) 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); @@ -3708,6 +3745,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 @@ -3763,7 +4431,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"); @@ -4340,7 +5008,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); @@ -4364,12 +5032,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); @@ -4384,7 +5052,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); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index a683e766d1ae..e66fa7fbd933 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -966,16 +966,16 @@ qla2xxx_get_idc_param(scsi_qla_host_t *vha) QLA82XX_IDC_PARAM_ADDR , 8); if (*wptr == __constant_cpu_to_le32(0xffffffff)) { - ha->nx_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; - ha->nx_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; + ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; + ha->fcoe_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; } else { - ha->nx_dev_init_timeout = le32_to_cpu(*wptr++); - ha->nx_reset_timeout = le32_to_cpu(*wptr); + ha->fcoe_dev_init_timeout = le32_to_cpu(*wptr++); + ha->fcoe_reset_timeout = le32_to_cpu(*wptr); } ql_dbg(ql_dbg_init, vha, 0x004e, - "nx_dev_init_timeout=%d " - "nx_reset_timeout=%d.\n", ha->nx_dev_init_timeout, - ha->nx_reset_timeout); + "fcoe_dev_init_timeout=%d " + "fcoe_reset_timeout=%d.\n", ha->fcoe_dev_init_timeout, + ha->fcoe_reset_timeout); return; } @@ -1017,7 +1017,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) return; - if (ha->flags.isp82xx_reset_hdlr_active) + if (ha->flags.nic_core_reset_hdlr_active) return; ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, @@ -1675,15 +1675,15 @@ qla83xx_beacon_blink(struct scsi_qla_host *vha) if (IS_QLA2031(ha) && ha->beacon_blink_led) { if (ha->flags.port0) - led_select_value = 0x00201320; + led_select_value = QLA83XX_LED_PORT0; else - led_select_value = 0x00201328; + led_select_value = QLA83XX_LED_PORT1; - qla83xx_write_remote_reg(vha, led_select_value, 0x40002000); - qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40002000); + qla83xx_wr_reg(vha, led_select_value, 0x40002000); + qla83xx_wr_reg(vha, led_select_value + 4, 0x40002000); msleep(1000); - qla83xx_write_remote_reg(vha, led_select_value, 0x40004000); - qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40004000); + qla83xx_wr_reg(vha, led_select_value, 0x40004000); + qla83xx_wr_reg(vha, led_select_value + 4, 0x40004000); } else if ((IS_QLA8031(ha) || IS_QLA81XX(ha)) && ha->beacon_blink_led) { int rval;