| /* |
| * QLogic Fibre Channel HBA Driver |
| * Copyright (c) 2003-2014 QLogic Corporation |
| * |
| * See LICENSE.qla2xxx for copyright and licensing details. |
| */ |
| #include "qla_def.h" |
| #include "qla_gbl.h" |
| |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/vmalloc.h> |
| |
| #include "qla_devtbl.h" |
| |
| #ifdef CONFIG_SPARC |
| #include <asm/prom.h> |
| #endif |
| |
| #include <target/target_core_base.h> |
| #include "qla_target.h" |
| |
| /* |
| * QLogic ISP2x00 Hardware Support Function Prototypes. |
| */ |
| static int qla2x00_isp_firmware(scsi_qla_host_t *); |
| static int qla2x00_setup_chip(scsi_qla_host_t *); |
| static int qla2x00_fw_ready(scsi_qla_host_t *); |
| static int qla2x00_configure_hba(scsi_qla_host_t *); |
| static int qla2x00_configure_loop(scsi_qla_host_t *); |
| static int qla2x00_configure_local_loop(scsi_qla_host_t *); |
| static int qla2x00_configure_fabric(scsi_qla_host_t *); |
| static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *); |
| static int qla2x00_restart_isp(scsi_qla_host_t *); |
| |
| static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *); |
| static int qla84xx_init_chip(scsi_qla_host_t *); |
| static int qla25xx_init_queues(struct qla_hw_data *); |
| static int qla24xx_post_prli_work(struct scsi_qla_host*, fc_port_t *); |
| static void qla24xx_handle_plogi_done_event(struct scsi_qla_host *, |
| struct event_arg *); |
| static void qla24xx_handle_prli_done_event(struct scsi_qla_host *, |
| struct event_arg *); |
| static void __qla24xx_handle_gpdb_event(scsi_qla_host_t *, struct event_arg *); |
| |
| /* SRB Extensions ---------------------------------------------------------- */ |
| |
| void |
| qla2x00_sp_timeout(struct timer_list *t) |
| { |
| srb_t *sp = from_timer(sp, t, u.iocb_cmd.timer); |
| struct srb_iocb *iocb; |
| struct req_que *req; |
| unsigned long flags; |
| struct qla_hw_data *ha = sp->vha->hw; |
| |
| WARN_ON_ONCE(irqs_disabled()); |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| req = sp->qpair->req; |
| req->outstanding_cmds[sp->handle] = NULL; |
| iocb = &sp->u.iocb_cmd; |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| iocb->timeout(sp); |
| } |
| |
| void |
| qla2x00_sp_free(void *ptr) |
| { |
| srb_t *sp = ptr; |
| struct srb_iocb *iocb = &sp->u.iocb_cmd; |
| |
| del_timer(&iocb->timer); |
| qla2x00_rel_sp(sp); |
| } |
| |
| /* Asynchronous Login/Logout Routines -------------------------------------- */ |
| |
| unsigned long |
| qla2x00_get_async_timeout(struct scsi_qla_host *vha) |
| { |
| unsigned long tmo; |
| struct qla_hw_data *ha = vha->hw; |
| |
| /* Firmware should use switch negotiated r_a_tov for timeout. */ |
| tmo = ha->r_a_tov / 10 * 2; |
| if (IS_QLAFX00(ha)) { |
| tmo = FX00_DEF_RATOV * 2; |
| } else if (!IS_FWI2_CAPABLE(ha)) { |
| /* |
| * Except for earlier ISPs where the timeout is seeded from the |
| * initialization control block. |
| */ |
| tmo = ha->login_timeout; |
| } |
| return tmo; |
| } |
| |
| void |
| qla2x00_async_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| fc_port_t *fcport = sp->fcport; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| int rc, h; |
| unsigned long flags; |
| |
| if (fcport) { |
| ql_dbg(ql_dbg_disc, fcport->vha, 0x2071, |
| "Async-%s timeout - hdl=%x portid=%06x %8phC.\n", |
| sp->name, sp->handle, fcport->d_id.b24, fcport->port_name); |
| |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| } else { |
| pr_info("Async-%s timeout - hdl=%x.\n", |
| sp->name, sp->handle); |
| } |
| |
| switch (sp->type) { |
| case SRB_LOGIN_CMD: |
| rc = qla24xx_async_abort_cmd(sp, false); |
| if (rc) { |
| /* Retry as needed. */ |
| lio->u.logio.data[0] = MBS_COMMAND_ERROR; |
| lio->u.logio.data[1] = |
| lio->u.logio.flags & SRB_LOGIN_RETRIED ? |
| QLA_LOGIO_LOGIN_RETRIED : 0; |
| spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); |
| for (h = 1; h < sp->qpair->req->num_outstanding_cmds; |
| h++) { |
| if (sp->qpair->req->outstanding_cmds[h] == |
| sp) { |
| sp->qpair->req->outstanding_cmds[h] = |
| NULL; |
| break; |
| } |
| } |
| spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); |
| sp->done(sp, QLA_FUNCTION_TIMEOUT); |
| } |
| break; |
| case SRB_LOGOUT_CMD: |
| case SRB_CT_PTHRU_CMD: |
| case SRB_MB_IOCB: |
| case SRB_NACK_PLOGI: |
| case SRB_NACK_PRLI: |
| case SRB_NACK_LOGO: |
| case SRB_CTRL_VP: |
| rc = qla24xx_async_abort_cmd(sp, false); |
| if (rc) { |
| spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); |
| for (h = 1; h < sp->qpair->req->num_outstanding_cmds; |
| h++) { |
| if (sp->qpair->req->outstanding_cmds[h] == |
| sp) { |
| sp->qpair->req->outstanding_cmds[h] = |
| NULL; |
| break; |
| } |
| } |
| spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); |
| sp->done(sp, QLA_FUNCTION_TIMEOUT); |
| } |
| break; |
| } |
| } |
| |
| static void |
| qla2x00_async_login_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct scsi_qla_host *vha = sp->vha; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20dd, |
| "%s %8phC res %d \n", __func__, sp->fcport->port_name, res); |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) { |
| memset(&ea, 0, sizeof(ea)); |
| ea.event = FCME_PLOGI_DONE; |
| ea.fcport = sp->fcport; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.sp = sp; |
| qla2x00_fcport_event_handler(vha, &ea); |
| } |
| |
| sp->free(sp); |
| } |
| |
| static inline bool |
| fcport_is_smaller(fc_port_t *fcport) |
| { |
| if (wwn_to_u64(fcport->port_name) < |
| wwn_to_u64(fcport->vha->port_name)) |
| return true; |
| else |
| return false; |
| } |
| |
| static inline bool |
| fcport_is_bigger(fc_port_t *fcport) |
| { |
| return !fcport_is_smaller(fcport); |
| } |
| |
| int |
| qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT) || |
| fcport->loop_id == FC_NO_LOOP_ID) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC - not sending command.\n", |
| __func__, fcport->port_name); |
| return rval; |
| } |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->logout_completed = 0; |
| |
| fcport->disc_state = DSC_LOGIN_PEND; |
| sp->type = SRB_LOGIN_CMD; |
| sp->name = "login"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| sp->done = qla2x00_async_login_sp_done; |
| if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport)) |
| lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY; |
| else |
| lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; |
| |
| if (fcport->fc4f_nvme) |
| lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2072, |
| "Async-login - %8phC hdl=%x, loopid=%x portid=%02x%02x%02x " |
| "retries=%d.\n", fcport->port_name, sp->handle, fcport->loop_id, |
| fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, |
| fcport->login_retry); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) { |
| fcport->flags |= FCF_LOGIN_NEEDED; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| goto done_free_sp; |
| } |
| |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return rval; |
| } |
| |
| static void |
| qla2x00_async_logout_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| sp->fcport->login_gen++; |
| qlt_logo_completion_handler(sp->fcport, res); |
| sp->free(sp); |
| } |
| |
| int |
| qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_LOGOUT_CMD; |
| sp->name = "logout"; |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| sp->done = qla2x00_async_logout_sp_done; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2070, |
| "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b.domain, |
| fcport->d_id.b.area, fcport->d_id.b.al_pa, |
| fcport->port_name); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| return rval; |
| } |
| |
| void |
| qla2x00_async_prlo_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| /* Don't re-login in target mode */ |
| if (!fcport->tgt_session) |
| qla2x00_mark_device_lost(vha, fcport, 1, 0); |
| qlt_logo_completion_handler(fcport, data[0]); |
| } |
| |
| static void |
| qla2x00_async_prlo_sp_done(void *s, int res) |
| { |
| srb_t *sp = (srb_t *)s; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct scsi_qla_host *vha = sp->vha; |
| |
| sp->fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) |
| qla2x00_post_async_prlo_done_work(sp->fcport->vha, sp->fcport, |
| lio->u.logio.data); |
| sp->free(sp); |
| } |
| |
| int |
| qla2x00_async_prlo(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval; |
| |
| rval = QLA_FUNCTION_FAILED; |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_PRLO_CMD; |
| sp->name = "prlo"; |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| sp->done = qla2x00_async_prlo_sp_done; |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2070, |
| "Async-prlo - hdl=%x loop-id=%x portid=%02x%02x%02x.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b.domain, |
| fcport->d_id.b.area, fcport->d_id.b.al_pa); |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return rval; |
| } |
| |
| static |
| void qla24xx_handle_adisc_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| struct fc_port *fcport = ea->fcport; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d2, |
| "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d lid %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, fcport->login_gen, ea->sp->gen2, |
| fcport->rscn_gen, ea->sp->gen1, fcport->loop_id); |
| |
| if (ea->data[0] != MBS_COMMAND_COMPLETE) { |
| ql_dbg(ql_dbg_disc, vha, 0x2066, |
| "%s %8phC: adisc fail: post delete\n", |
| __func__, ea->fcport->port_name); |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| return; |
| } |
| |
| if (ea->fcport->disc_state == DSC_DELETE_PEND) |
| return; |
| |
| if (ea->sp->gen2 != ea->fcport->login_gen) { |
| /* target side must have changed it. */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed\n", |
| __func__, ea->fcport->port_name); |
| return; |
| } else if (ea->sp->gen1 != ea->fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d4, "%s %d %8phC post gidpn\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| qla24xx_post_gidpn_work(vha, ea->fcport); |
| return; |
| } |
| |
| __qla24xx_handle_gpdb_event(vha, ea); |
| } |
| |
| int qla_post_els_plogi_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_ELS_PLOGI); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| fcport->disc_state = DSC_LOGIN_PEND; |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static void |
| qla2x00_async_adisc_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct scsi_qla_host *vha = sp->vha; |
| struct event_arg ea; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2066, |
| "Async done-%s res %x %8phC\n", |
| sp->name, res, sp->fcport->port_name); |
| |
| sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| memset(&ea, 0, sizeof(ea)); |
| ea.event = FCME_ADISC_DONE; |
| ea.rc = res; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.fcport = sp->fcport; |
| ea.sp = sp; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| |
| sp->free(sp); |
| } |
| |
| int |
| qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval; |
| |
| rval = QLA_FUNCTION_FAILED; |
| fcport->flags |= FCF_ASYNC_SENT; |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_ADISC_CMD; |
| sp->name = "adisc"; |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| sp->done = qla2x00_async_adisc_sp_done; |
| if (data[1] & QLA_LOGIO_LOGIN_RETRIED) |
| lio->u.logio.flags |= SRB_LOGIN_RETRIED; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x206f, |
| "Async-adisc - hdl=%x loopid=%x portid=%06x %8phC.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b24, fcport->port_name); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| qla2x00_post_async_adisc_work(vha, fcport, data); |
| return rval; |
| } |
| |
| static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport, *conflict_fcport; |
| struct get_name_list_extended *e; |
| u16 i, n, found = 0, loop_id; |
| port_id_t id; |
| u64 wwn; |
| u16 data[2]; |
| u8 current_login_state; |
| |
| fcport = ea->fcport; |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s %8phC DS %d LS rc %d %d login %d|%d rscn %d|%d lid %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, |
| fcport->login_gen, fcport->last_login_gen, |
| fcport->rscn_gen, fcport->last_rscn_gen, vha->loop_id); |
| |
| if (fcport->disc_state == DSC_DELETE_PEND) |
| return; |
| |
| if (ea->rc) { /* rval */ |
| if (fcport->login_retry == 0) { |
| ql_dbg(ql_dbg_disc, vha, 0x20de, |
| "GNL failed Port login retry %8phN, retry cnt=%d.\n", |
| fcport->port_name, fcport->login_retry); |
| } |
| return; |
| } |
| |
| if (fcport->last_rscn_gen != fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20df, |
| "%s %8phC rscn gen changed rscn %d|%d \n", |
| __func__, fcport->port_name, |
| fcport->last_rscn_gen, fcport->rscn_gen); |
| qla24xx_post_gidpn_work(vha, fcport); |
| return; |
| } else if (fcport->last_login_gen != fcport->login_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e0, |
| "%s %8phC login gen changed\n", |
| __func__, fcport->port_name); |
| return; |
| } |
| |
| n = ea->data[0] / sizeof(struct get_name_list_extended); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e1, |
| "%s %d %8phC n %d %02x%02x%02x lid %d \n", |
| __func__, __LINE__, fcport->port_name, n, |
| fcport->d_id.b.domain, fcport->d_id.b.area, |
| fcport->d_id.b.al_pa, fcport->loop_id); |
| |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| wwn = wwn_to_u64(e->port_name); |
| id.b.domain = e->port_id[2]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[0]; |
| id.b.rsvd_1 = 0; |
| |
| if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE)) |
| continue; |
| |
| if (IS_SW_RESV_ADDR(id)) |
| continue; |
| |
| found = 1; |
| |
| loop_id = le16_to_cpu(e->nport_handle); |
| loop_id = (loop_id & 0x7fff); |
| if (fcport->fc4f_nvme) |
| current_login_state = e->current_login_state >> 4; |
| else |
| current_login_state = e->current_login_state & 0xf; |
| |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e2, |
| "%s found %8phC CLS [%x|%x] nvme %d ID[%02x%02x%02x|%02x%02x%02x] lid[%d|%d]\n", |
| __func__, fcport->port_name, |
| e->current_login_state, fcport->fw_login_state, |
| fcport->fc4f_nvme, id.b.domain, id.b.area, id.b.al_pa, |
| fcport->d_id.b.domain, fcport->d_id.b.area, |
| fcport->d_id.b.al_pa, loop_id, fcport->loop_id); |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETE_PEND: |
| case DSC_DELETED: |
| break; |
| default: |
| if ((id.b24 != fcport->d_id.b24 && |
| fcport->d_id.b24) || |
| (fcport->loop_id != FC_NO_LOOP_ID && |
| fcport->loop_id != loop_id)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e3, |
| "%s %d %8phC post del sess\n", |
| __func__, __LINE__, fcport->port_name); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| break; |
| } |
| |
| fcport->loop_id = loop_id; |
| |
| wwn = wwn_to_u64(fcport->port_name); |
| qlt_find_sess_invalidate_other(vha, wwn, |
| id, loop_id, &conflict_fcport); |
| |
| if (conflict_fcport) { |
| /* |
| * Another share fcport share the same loop_id & |
| * nport id. Conflict fcport needs to finish |
| * cleanup before this fcport can proceed to login. |
| */ |
| conflict_fcport->conflict = fcport; |
| fcport->login_pause = 1; |
| } |
| |
| switch (vha->hw->current_topology) { |
| default: |
| switch (current_login_state) { |
| case DSC_LS_PRLI_COMP: |
| ql_dbg(ql_dbg_disc + ql_dbg_verbose, |
| vha, 0x20e4, "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) |
| fcport->port_type = FCT_INITIATOR; |
| else |
| fcport->port_type = FCT_TARGET; |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, |
| data); |
| break; |
| case DSC_LS_PORT_UNAVAIL: |
| default: |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| qla2x00_find_new_loop_id(vha, fcport); |
| fcport->fw_login_state = |
| DSC_LS_PORT_UNAVAIL; |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x20e5, |
| "%s %d %8phC\n", __func__, __LINE__, |
| fcport->port_name); |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| } |
| break; |
| case ISP_CFG_N: |
| fcport->fw_login_state = current_login_state; |
| fcport->d_id = id; |
| switch (current_login_state) { |
| case DSC_LS_PRLI_COMP: |
| if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) |
| fcport->port_type = FCT_INITIATOR; |
| else |
| fcport->port_type = FCT_TARGET; |
| |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, |
| data); |
| break; |
| case DSC_LS_PLOGI_COMP: |
| if (fcport_is_bigger(fcport)) { |
| /* local adapter is smaller */ |
| if (fcport->loop_id != FC_NO_LOOP_ID) |
| qla2x00_clear_loop_id(fcport); |
| |
| fcport->loop_id = loop_id; |
| qla24xx_fcport_handle_login(vha, |
| fcport); |
| break; |
| } |
| /* drop through */ |
| default: |
| if (fcport_is_smaller(fcport)) { |
| /* local adapter is bigger */ |
| if (fcport->loop_id != FC_NO_LOOP_ID) |
| qla2x00_clear_loop_id(fcport); |
| |
| fcport->loop_id = loop_id; |
| qla24xx_fcport_handle_login(vha, |
| fcport); |
| } |
| break; |
| } |
| break; |
| } /* switch (ha->current_topology) */ |
| } |
| |
| if (!found) { |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_F: |
| case ISP_CFG_FL: |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| id.b.domain = e->port_id[0]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[2]; |
| id.b.rsvd_1 = 0; |
| loop_id = le16_to_cpu(e->nport_handle); |
| |
| if (fcport->d_id.b24 == id.b24) { |
| conflict_fcport = |
| qla2x00_find_fcport_by_wwpn(vha, |
| e->port_name, 0); |
| if (conflict_fcport) { |
| ql_dbg(ql_dbg_disc + ql_dbg_verbose, |
| vha, 0x20e5, |
| "%s %d %8phC post del sess\n", |
| __func__, __LINE__, |
| conflict_fcport->port_name); |
| qlt_schedule_sess_for_deletion |
| (conflict_fcport); |
| } |
| } |
| /* |
| * FW already picked this loop id for |
| * another fcport |
| */ |
| if (fcport->loop_id == loop_id) |
| fcport->loop_id = FC_NO_LOOP_ID; |
| } |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| case ISP_CFG_N: |
| fcport->disc_state = DSC_DELETED; |
| if (time_after_eq(jiffies, fcport->dm_login_expire)) { |
| if (fcport->n2n_link_reset_cnt < 2) { |
| fcport->n2n_link_reset_cnt++; |
| /* |
| * remote port is not sending PLOGI. |
| * Reset link to kick start his state |
| * machine |
| */ |
| set_bit(N2N_LINK_RESET, |
| &vha->dpc_flags); |
| } else { |
| if (fcport->n2n_chip_reset < 1) { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Chip reset to bring laser down"); |
| set_bit(ISP_ABORT_NEEDED, |
| &vha->dpc_flags); |
| fcport->n2n_chip_reset++; |
| } else { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Remote port %8ph is not coming back\n", |
| fcport->port_name); |
| fcport->scan_state = 0; |
| } |
| } |
| qla2xxx_wake_dpc(vha); |
| } else { |
| /* |
| * report port suppose to do PLOGI. Give him |
| * more time. FW will catch it. |
| */ |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } /* gnl_event */ |
| |
| static void |
| qla24xx_async_gnl_sp_done(void *s, int res) |
| { |
| struct srb *sp = s; |
| struct scsi_qla_host *vha = sp->vha; |
| unsigned long flags; |
| struct fc_port *fcport = NULL, *tf; |
| u16 i, n = 0, loop_id; |
| struct event_arg ea; |
| struct get_name_list_extended *e; |
| u64 wwn; |
| struct list_head h; |
| bool found = false; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e7, |
| "Async done-%s res %x mb[1]=%x mb[2]=%x \n", |
| sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1], |
| sp->u.iocb_cmd.u.mbx.in_mb[2]); |
| |
| if (res == QLA_FUNCTION_TIMEOUT) |
| return; |
| |
| memset(&ea, 0, sizeof(ea)); |
| ea.sp = sp; |
| ea.rc = res; |
| ea.event = FCME_GNL_DONE; |
| |
| if (sp->u.iocb_cmd.u.mbx.in_mb[1] >= |
| sizeof(struct get_name_list_extended)) { |
| n = sp->u.iocb_cmd.u.mbx.in_mb[1] / |
| sizeof(struct get_name_list_extended); |
| ea.data[0] = sp->u.iocb_cmd.u.mbx.in_mb[1]; /* amnt xfered */ |
| } |
| |
| for (i = 0; i < n; i++) { |
| e = &vha->gnl.l[i]; |
| loop_id = le16_to_cpu(e->nport_handle); |
| /* mask out reserve bit */ |
| loop_id = (loop_id & 0x7fff); |
| set_bit(loop_id, vha->hw->loop_id_map); |
| wwn = wwn_to_u64(e->port_name); |
| |
| ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0x20e8, |
| "%s %8phC %02x:%02x:%02x state %d/%d lid %x \n", |
| __func__, (void *)&wwn, e->port_id[2], e->port_id[1], |
| e->port_id[0], e->current_login_state, e->last_login_state, |
| (loop_id & 0x7fff)); |
| } |
| |
| spin_lock_irqsave(&vha->gnl.fcports_lock, flags); |
| |
| INIT_LIST_HEAD(&h); |
| fcport = tf = NULL; |
| if (!list_empty(&vha->gnl.fcports)) |
| list_splice_init(&vha->gnl.fcports, &h); |
| |
| list_for_each_entry_safe(fcport, tf, &h, gnl_entry) { |
| list_del_init(&fcport->gnl_entry); |
| spin_lock(&vha->hw->tgt.sess_lock); |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| spin_unlock(&vha->hw->tgt.sess_lock); |
| ea.fcport = fcport; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| } |
| spin_unlock_irqrestore(&vha->gnl.fcports_lock, flags); |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| /* create new fcport if fw has knowledge of new sessions */ |
| for (i = 0; i < n; i++) { |
| port_id_t id; |
| u64 wwnn; |
| |
| e = &vha->gnl.l[i]; |
| wwn = wwn_to_u64(e->port_name); |
| |
| found = false; |
| list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { |
| if (!memcmp((u8 *)&wwn, fcport->port_name, |
| WWN_SIZE)) { |
| found = true; |
| break; |
| } |
| } |
| |
| id.b.domain = e->port_id[2]; |
| id.b.area = e->port_id[1]; |
| id.b.al_pa = e->port_id[0]; |
| id.b.rsvd_1 = 0; |
| |
| if (!found && wwn && !IS_SW_RESV_ADDR(id)) { |
| ql_dbg(ql_dbg_disc, vha, 0x2065, |
| "%s %d %8phC %06x post new sess\n", |
| __func__, __LINE__, (u8 *)&wwn, id.b24); |
| wwnn = wwn_to_u64(e->node_name); |
| qla24xx_post_newsess_work(vha, &id, (u8 *)&wwn, |
| (u8 *)&wwnn, NULL, FC4_TYPE_UNKNOWN); |
| } |
| } |
| |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| sp->free(sp); |
| } |
| |
| int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| struct srb_iocb *mbx; |
| int rval = QLA_FUNCTION_FAILED; |
| unsigned long flags; |
| u16 *mb; |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) |
| return rval; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d9, |
| "Async-gnlist WWPN %8phC \n", fcport->port_name); |
| |
| spin_lock_irqsave(&vha->gnl.fcports_lock, flags); |
| if (!list_empty(&fcport->gnl_entry)) { |
| spin_unlock_irqrestore(&vha->gnl.fcports_lock, flags); |
| rval = QLA_SUCCESS; |
| goto done; |
| } |
| |
| spin_lock(&vha->hw->tgt.sess_lock); |
| fcport->disc_state = DSC_GNL; |
| fcport->last_rscn_gen = fcport->rscn_gen; |
| fcport->last_login_gen = fcport->login_gen; |
| spin_unlock(&vha->hw->tgt.sess_lock); |
| |
| list_add_tail(&fcport->gnl_entry, &vha->gnl.fcports); |
| spin_unlock_irqrestore(&vha->gnl.fcports_lock, flags); |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gnlist"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| |
| mbx = &sp->u.iocb_cmd; |
| mbx->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2); |
| |
| mb = sp->u.iocb_cmd.u.mbx.out_mb; |
| mb[0] = MBC_PORT_NODE_NAME_LIST; |
| mb[1] = BIT_2 | BIT_3; |
| mb[2] = MSW(vha->gnl.ldma); |
| mb[3] = LSW(vha->gnl.ldma); |
| mb[6] = MSW(MSD(vha->gnl.ldma)); |
| mb[7] = LSW(MSD(vha->gnl.ldma)); |
| mb[8] = vha->gnl.size; |
| mb[9] = vha->vp_idx; |
| |
| sp->done = qla24xx_async_gnl_sp_done; |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20da, |
| "Async-%s - OUT WWPN %8phC hndl %x\n", |
| sp->name, fcport->port_name, sp->handle); |
| |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| return rval; |
| } |
| |
| int qla24xx_post_gnl_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_GNL); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| fcport->disc_state = DSC_LOGIN_PEND; |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static |
| void qla24xx_async_gpdb_sp_done(void *s, int res) |
| { |
| struct srb *sp = s; |
| struct scsi_qla_host *vha = sp->vha; |
| struct qla_hw_data *ha = vha->hw; |
| fc_port_t *fcport = sp->fcport; |
| u16 *mb = sp->u.iocb_cmd.u.mbx.in_mb; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20db, |
| "Async done-%s res %x, WWPN %8phC mb[1]=%x mb[2]=%x \n", |
| sp->name, res, fcport->port_name, mb[1], mb[2]); |
| |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| |
| if (res == QLA_FUNCTION_TIMEOUT) |
| goto done; |
| |
| memset(&ea, 0, sizeof(ea)); |
| ea.event = FCME_GPDB_DONE; |
| ea.fcport = fcport; |
| ea.sp = sp; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| |
| done: |
| dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in, |
| sp->u.iocb_cmd.u.mbx.in_dma); |
| |
| sp->free(sp); |
| } |
| |
| static int qla24xx_post_prli_work(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_PRLI); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static void |
| qla2x00_async_prli_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct scsi_qla_host *vha = sp->vha; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| struct event_arg ea; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2129, |
| "%s %8phC res %d \n", __func__, |
| sp->fcport->port_name, res); |
| |
| sp->fcport->flags &= ~FCF_ASYNC_SENT; |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) { |
| memset(&ea, 0, sizeof(ea)); |
| ea.event = FCME_PRLI_DONE; |
| ea.fcport = sp->fcport; |
| ea.data[0] = lio->u.logio.data[0]; |
| ea.data[1] = lio->u.logio.data[1]; |
| ea.iop[0] = lio->u.logio.iop[0]; |
| ea.iop[1] = lio->u.logio.iop[1]; |
| ea.sp = sp; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| } |
| |
| sp->free(sp); |
| } |
| |
| int |
| qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| srb_t *sp; |
| struct srb_iocb *lio; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| if (!vha->flags.online) |
| return rval; |
| |
| if (fcport->fw_login_state == DSC_LS_PLOGI_PEND || |
| fcport->fw_login_state == DSC_LS_PRLI_PEND) |
| return rval; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| return rval; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->logout_completed = 0; |
| |
| sp->type = SRB_PRLI_CMD; |
| sp->name = "prli"; |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| sp->done = qla2x00_async_prli_sp_done; |
| lio->u.logio.flags = 0; |
| |
| if (fcport->fc4f_nvme) |
| lio->u.logio.flags |= SRB_LOGIN_NVME_PRLI; |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) { |
| fcport->flags |= FCF_LOGIN_NEEDED; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| goto done_free_sp; |
| } |
| |
| ql_dbg(ql_dbg_disc, vha, 0x211b, |
| "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n", |
| fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24, |
| fcport->login_retry, fcport->fc4f_nvme ? "nvme" : "fc"); |
| |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| return rval; |
| } |
| |
| int qla24xx_post_gpdb_work(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) |
| { |
| struct qla_work_evt *e; |
| |
| e = qla2x00_alloc_work(vha, QLA_EVT_GPDB); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.fcport.fcport = fcport; |
| e->u.fcport.opt = opt; |
| fcport->flags |= FCF_ASYNC_ACTIVE; |
| return qla2x00_post_work(vha, e); |
| } |
| |
| int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) |
| { |
| srb_t *sp; |
| struct srb_iocb *mbx; |
| int rval = QLA_FUNCTION_FAILED; |
| u16 *mb; |
| dma_addr_t pd_dma; |
| struct port_database_24xx *pd; |
| struct qla_hw_data *ha = vha->hw; |
| |
| if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT) || |
| fcport->loop_id == FC_NO_LOOP_ID) { |
| ql_log(ql_log_warn, vha, 0xffff, |
| "%s: %8phC - not sending command.\n", |
| __func__, fcport->port_name); |
| return rval; |
| } |
| |
| fcport->disc_state = DSC_GPDB; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gpdb"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| |
| mbx = &sp->u.iocb_cmd; |
| mbx->timeout = qla2x00_async_iocb_timeout; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| pd = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma); |
| if (pd == NULL) { |
| ql_log(ql_log_warn, vha, 0xd043, |
| "Failed to allocate port database structure.\n"); |
| goto done_free_sp; |
| } |
| |
| mb = sp->u.iocb_cmd.u.mbx.out_mb; |
| mb[0] = MBC_GET_PORT_DATABASE; |
| mb[1] = fcport->loop_id; |
| mb[2] = MSW(pd_dma); |
| mb[3] = LSW(pd_dma); |
| mb[6] = MSW(MSD(pd_dma)); |
| mb[7] = LSW(MSD(pd_dma)); |
| mb[9] = vha->vp_idx; |
| mb[10] = opt; |
| |
| mbx->u.mbx.in = (void *)pd; |
| mbx->u.mbx.in_dma = pd_dma; |
| |
| sp->done = qla24xx_async_gpdb_sp_done; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20dc, |
| "Async-%s %8phC hndl %x opt %x\n", |
| sp->name, fcport->port_name, sp->handle, opt); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| return rval; |
| |
| done_free_sp: |
| if (pd) |
| dma_pool_free(ha->s_dma_pool, pd, pd_dma); |
| |
| sp->free(sp); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| qla24xx_post_gpdb_work(vha, fcport, opt); |
| return rval; |
| } |
| |
| static |
| void __qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| ea->fcport->login_gen++; |
| ea->fcport->deleted = 0; |
| ea->fcport->logout_on_delete = 1; |
| |
| if (!ea->fcport->login_succ && !IS_SW_RESV_ADDR(ea->fcport->d_id)) { |
| vha->fcport_count++; |
| ea->fcport->login_succ = 1; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d6, |
| "%s %d %8phC post upd_fcport fcp_cnt %d\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| vha->fcport_count); |
| qla24xx_post_upd_fcport_work(vha, ea->fcport); |
| } else if (ea->fcport->login_succ) { |
| /* |
| * We have an existing session. A late RSCN delivery |
| * must have triggered the session to be re-validate. |
| * Session is still valid. |
| */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d6, |
| "%s %d %8phC session revalidate success\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| ea->fcport->disc_state = DSC_LOGIN_COMPLETE; |
| } |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| } |
| |
| static |
| void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| struct port_database_24xx *pd; |
| struct srb *sp = ea->sp; |
| uint8_t ls; |
| |
| pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in; |
| |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d2, |
| "%s %8phC DS %d LS %d nvme %x rc %d\n", __func__, fcport->port_name, |
| fcport->disc_state, pd->current_login_state, fcport->fc4f_nvme, |
| ea->rc); |
| |
| if (fcport->disc_state == DSC_DELETE_PEND) |
| return; |
| |
| if (fcport->fc4f_nvme) |
| ls = pd->current_login_state >> 4; |
| else |
| ls = pd->current_login_state & 0xf; |
| |
| switch (ls) { |
| case PDS_PRLI_COMPLETE: |
| __qla24xx_parse_gpdb(vha, fcport, pd); |
| break; |
| case PDS_PLOGI_PENDING: |
| case PDS_PLOGI_COMPLETE: |
| case PDS_PRLI_PENDING: |
| case PDS_PRLI2_PENDING: |
| /* Set discovery state back to GNL to Relogin attempt */ |
| if (qla_dual_mode_enabled(vha) || |
| qla_ini_mode_enabled(vha)) { |
| fcport->disc_state = DSC_GNL; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| } |
| return; |
| case PDS_LOGO_PENDING: |
| case PDS_PORT_UNAVAILABLE: |
| default: |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC post del sess\n", |
| __func__, __LINE__, fcport->port_name); |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| __qla24xx_handle_gpdb_event(vha, ea); |
| } /* gpdb event */ |
| |
| static void qla_chk_n2n_b4_login(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| u8 login = 0; |
| int rc; |
| |
| if (qla_tgt_mode_enabled(vha)) |
| return; |
| |
| if (qla_dual_mode_enabled(vha)) { |
| if (N2N_TOPO(vha->hw)) { |
| u64 mywwn, wwn; |
| |
| mywwn = wwn_to_u64(vha->port_name); |
| wwn = wwn_to_u64(fcport->port_name); |
| if (mywwn > wwn) |
| login = 1; |
| else if ((fcport->fw_login_state == DSC_LS_PLOGI_COMP) |
| && time_after_eq(jiffies, |
| fcport->plogi_nack_done_deadline)) |
| login = 1; |
| } else { |
| login = 1; |
| } |
| } else { |
| /* initiator mode */ |
| login = 1; |
| } |
| |
| if (login) { |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; |
| rc = qla2x00_find_new_loop_id(vha, fcport); |
| if (rc) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e6, |
| "%s %d %8phC post del sess - out of loopid\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->scan_state = 0; |
| qlt_schedule_sess_for_deletion(fcport); |
| return; |
| } |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x20bf, |
| "%s %d %8phC post login\n", |
| __func__, __LINE__, fcport->port_name); |
| qla2x00_post_async_login_work(vha, fcport, NULL); |
| } |
| } |
| |
| int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| u16 data[2]; |
| u64 wwn; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d8, |
| "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d retry %d lid %d scan %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, fcport->login_pause, fcport->flags, |
| fcport->conflict, fcport->last_rscn_gen, fcport->rscn_gen, |
| fcport->login_gen, fcport->login_retry, |
| fcport->loop_id, fcport->scan_state); |
| |
| if (fcport->scan_state != QLA_FCPORT_FOUND) |
| return 0; |
| |
| if ((fcport->loop_id != FC_NO_LOOP_ID) && |
| ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND))) |
| return 0; |
| |
| if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) { |
| if (time_before_eq(jiffies, fcport->plogi_nack_done_deadline)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return 0; |
| } |
| } |
| |
| /* for pure Target Mode. Login will not be initiated */ |
| if (vha->host->active_mode == MODE_TARGET) |
| return 0; |
| |
| if (fcport->flags & FCF_ASYNC_SENT) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return 0; |
| } |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETED: |
| wwn = wwn_to_u64(fcport->node_name); |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_N: |
| if (fcport_is_smaller(fcport)) { |
| /* this adapter is bigger */ |
| if (fcport->login_retry) { |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| qla2x00_find_new_loop_id(vha, |
| fcport); |
| fcport->fw_login_state = |
| DSC_LS_PORT_UNAVAIL; |
| } |
| fcport->login_retry--; |
| qla_post_els_plogi_work(vha, fcport); |
| } else { |
| ql_log(ql_log_info, vha, 0x705d, |
| "Unable to reach remote port %8phC", |
| fcport->port_name); |
| } |
| } else { |
| qla24xx_post_gnl_work(vha, fcport); |
| } |
| break; |
| default: |
| if (wwn == 0) { |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s %d %8phC post GNNID\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_gnnid_work(vha, fcport); |
| } else if (fcport->loop_id == FC_NO_LOOP_ID) { |
| ql_dbg(ql_dbg_disc, vha, 0x20bd, |
| "%s %d %8phC post gnl\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_gnl_work(vha, fcport); |
| } else { |
| qla_chk_n2n_b4_login(vha, fcport); |
| } |
| break; |
| } |
| break; |
| |
| case DSC_GNL: |
| switch (vha->hw->current_topology) { |
| case ISP_CFG_N: |
| if ((fcport->current_login_state & 0xf) == 0x6) { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post GPDB work\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->chip_reset = |
| vha->hw->base_qpair->chip_reset; |
| qla24xx_post_gpdb_work(vha, fcport, 0); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post NVMe PRLI\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_prli_work(vha, fcport); |
| } |
| break; |
| default: |
| if (fcport->login_pause) { |
| fcport->last_rscn_gen = fcport->rscn_gen; |
| fcport->last_login_gen = fcport->login_gen; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| break; |
| } |
| qla_chk_n2n_b4_login(vha, fcport); |
| break; |
| } |
| break; |
| |
| case DSC_LOGIN_FAILED: |
| fcport->login_retry--; |
| ql_dbg(ql_dbg_disc, vha, 0x20d0, |
| "%s %d %8phC post gidpn\n", |
| __func__, __LINE__, fcport->port_name); |
| if (N2N_TOPO(vha->hw)) |
| qla_chk_n2n_b4_login(vha, fcport); |
| else |
| qla24xx_post_gidpn_work(vha, fcport); |
| break; |
| |
| case DSC_LOGIN_COMPLETE: |
| /* recheck login state */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d1, |
| "%s %d %8phC post adisc\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->login_retry--; |
| data[0] = data[1] = 0; |
| qla2x00_post_async_adisc_work(vha, fcport, data); |
| break; |
| |
| case DSC_LOGIN_PEND: |
| if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) |
| qla24xx_post_prli_work(vha, fcport); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static |
| void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea) |
| { |
| fcport->rscn_gen++; |
| |
| ql_dbg(ql_dbg_disc, fcport->vha, 0x210c, |
| "%s %8phC DS %d LS %d\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state); |
| |
| if (fcport->flags & FCF_ASYNC_SENT) |
| return; |
| |
| switch (fcport->disc_state) { |
| case DSC_DELETED: |
| case DSC_LOGIN_COMPLETE: |
| qla24xx_post_gpnid_work(fcport->vha, &ea->id); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, |
| u8 *port_name, u8 *node_name, void *pla, u8 fc4_type) |
| { |
| struct qla_work_evt *e; |
| e = qla2x00_alloc_work(vha, QLA_EVT_NEW_SESS); |
| if (!e) |
| return QLA_FUNCTION_FAILED; |
| |
| e->u.new_sess.id = *id; |
| e->u.new_sess.pla = pla; |
| e->u.new_sess.fc4_type = fc4_type; |
| memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); |
| if (node_name) |
| memcpy(e->u.new_sess.node_name, node_name, WWN_SIZE); |
| |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static |
| void qla24xx_handle_relogin_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x2102, |
| "%s %8phC DS %d LS %d P %d del %d cnfl %p rscn %d|%d login %d|%d fl %x\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, fcport->login_pause, |
| fcport->deleted, fcport->conflict, |
| fcport->last_rscn_gen, fcport->rscn_gen, |
| fcport->last_login_gen, fcport->login_gen, |
| fcport->flags); |
| |
| if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND)) |
| return; |
| |
| if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) { |
| if (time_before_eq(jiffies, fcport->plogi_nack_done_deadline)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } |
| } |
| |
| if (fcport->last_rscn_gen != fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20e9, "%s %d %8phC post gidpn\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| qla24xx_post_gidpn_work(vha, fcport); |
| return; |
| } |
| |
| qla24xx_fcport_handle_login(vha, fcport); |
| } |
| |
| |
| void qla_handle_els_plogi_done(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post PRLI\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| qla24xx_post_prli_work(vha, ea->fcport); |
| } |
| |
| void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| fc_port_t *f, *tf; |
| uint32_t id = 0, mask, rid; |
| unsigned long flags; |
| fc_port_t *fcport; |
| |
| switch (ea->event) { |
| case FCME_RELOGIN: |
| if (test_bit(UNLOADING, &vha->dpc_flags)) |
| return; |
| |
| qla24xx_handle_relogin_event(vha, ea); |
| break; |
| case FCME_RSCN: |
| if (test_bit(UNLOADING, &vha->dpc_flags)) |
| return; |
| switch (ea->id.b.rsvd_1) { |
| case RSCN_PORT_ADDR: |
| fcport = qla2x00_find_fcport_by_nportid |
| (vha, &ea->id, 1); |
| if (fcport) |
| fcport->rscn_rcvd = 1; |
| |
| spin_lock_irqsave(&vha->work_lock, flags); |
| if (vha->scan.scan_flags == 0) { |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s: schedule\n", __func__); |
| vha->scan.scan_flags |= SF_QUEUED; |
| schedule_delayed_work(&vha->scan.scan_work, 5); |
| } |
| spin_unlock_irqrestore(&vha->work_lock, flags); |
| |
| break; |
| case RSCN_AREA_ADDR: |
| case RSCN_DOM_ADDR: |
| if (ea->id.b.rsvd_1 == RSCN_AREA_ADDR) { |
| mask = 0xffff00; |
| ql_dbg(ql_dbg_async, vha, 0x5044, |
| "RSCN: Area 0x%06x was affected\n", |
| ea->id.b24); |
| } else { |
| mask = 0xff0000; |
| ql_dbg(ql_dbg_async, vha, 0x507a, |
| "RSCN: Domain 0x%06x was affected\n", |
| ea->id.b24); |
| } |
| |
| rid = ea->id.b24 & mask; |
| list_for_each_entry_safe(f, tf, &vha->vp_fcports, |
| list) { |
| id = f->d_id.b24 & mask; |
| if (rid == id) { |
| ea->fcport = f; |
| qla24xx_handle_rscn_event(f, ea); |
| } |
| } |
| break; |
| case RSCN_FAB_ADDR: |
| default: |
| ql_log(ql_log_warn, vha, 0xd045, |
| "RSCN: Fabric was affected. Addr format %d\n", |
| ea->id.b.rsvd_1); |
| qla2x00_mark_all_devices_lost(vha, 1); |
| set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); |
| set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); |
| } |
| break; |
| case FCME_GIDPN_DONE: |
| qla24xx_handle_gidpn_event(vha, ea); |
| break; |
| case FCME_GNL_DONE: |
| qla24xx_handle_gnl_done_event(vha, ea); |
| break; |
| case FCME_GPSC_DONE: |
| qla24xx_handle_gpsc_event(vha, ea); |
| break; |
| case FCME_PLOGI_DONE: /* Initiator side sent LLIOCB */ |
| qla24xx_handle_plogi_done_event(vha, ea); |
| break; |
| case FCME_PRLI_DONE: |
| qla24xx_handle_prli_done_event(vha, ea); |
| break; |
| case FCME_GPDB_DONE: |
| qla24xx_handle_gpdb_event(vha, ea); |
| break; |
| case FCME_GPNID_DONE: |
| qla24xx_handle_gpnid_event(vha, ea); |
| break; |
| case FCME_GFFID_DONE: |
| qla24xx_handle_gffid_event(vha, ea); |
| break; |
| case FCME_ADISC_DONE: |
| qla24xx_handle_adisc_event(vha, ea); |
| break; |
| case FCME_GNNID_DONE: |
| qla24xx_handle_gnnid_event(vha, ea); |
| break; |
| case FCME_GFPNID_DONE: |
| qla24xx_handle_gfpnid_event(vha, ea); |
| break; |
| case FCME_ELS_PLOGI_DONE: |
| qla_handle_els_plogi_done(vha, ea); |
| break; |
| default: |
| BUG_ON(1); |
| break; |
| } |
| } |
| |
| static void |
| qla2x00_tmf_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| struct srb_iocb *tmf = &sp->u.iocb_cmd; |
| |
| tmf->u.tmf.comp_status = CS_TIMEOUT; |
| complete(&tmf->u.tmf.comp); |
| } |
| |
| static void |
| qla2x00_tmf_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct srb_iocb *tmf = &sp->u.iocb_cmd; |
| |
| complete(&tmf->u.tmf.comp); |
| } |
| |
| int |
| qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun, |
| uint32_t tag) |
| { |
| struct scsi_qla_host *vha = fcport->vha; |
| struct srb_iocb *tm_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| tm_iocb = &sp->u.iocb_cmd; |
| sp->type = SRB_TM_CMD; |
| sp->name = "tmf"; |
| |
| tm_iocb->timeout = qla2x00_tmf_iocb_timeout; |
| init_completion(&tm_iocb->u.tmf.comp); |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)); |
| |
| tm_iocb->u.tmf.flags = flags; |
| tm_iocb->u.tmf.lun = lun; |
| tm_iocb->u.tmf.data = tag; |
| sp->done = qla2x00_tmf_sp_done; |
| |
| ql_dbg(ql_dbg_taskm, vha, 0x802f, |
| "Async-tmf hdl=%x loop-id=%x portid=%02x%02x%02x.\n", |
| sp->handle, fcport->loop_id, fcport->d_id.b.domain, |
| fcport->d_id.b.area, fcport->d_id.b.al_pa); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| wait_for_completion(&tm_iocb->u.tmf.comp); |
| |
| rval = tm_iocb->u.tmf.data; |
| |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x8030, |
| "TM IOCB failed (%x).\n", rval); |
| } |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags) && !IS_QLAFX00(vha->hw)) { |
| flags = tm_iocb->u.tmf.flags; |
| lun = (uint16_t)tm_iocb->u.tmf.lun; |
| |
| /* Issue Marker IOCB */ |
| qla2x00_marker(vha, vha->hw->req_q_map[0], |
| vha->hw->rsp_q_map[0], fcport->loop_id, lun, |
| flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); |
| } |
| |
| done_free_sp: |
| sp->free(sp); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| done: |
| return rval; |
| } |
| |
| static void |
| qla24xx_abort_iocb_timeout(void *data) |
| { |
| srb_t *sp = data; |
| struct srb_iocb *abt = &sp->u.iocb_cmd; |
| |
| abt->u.abt.comp_status = CS_TIMEOUT; |
| sp->done(sp, QLA_FUNCTION_TIMEOUT); |
| } |
| |
| static void |
| qla24xx_abort_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct srb_iocb *abt = &sp->u.iocb_cmd; |
| |
| if (del_timer(&sp->u.iocb_cmd.timer)) { |
| if (sp->flags & SRB_WAKEUP_ON_COMP) |
| complete(&abt->u.abt.comp); |
| else |
| sp->free(sp); |
| } |
| } |
| |
| int |
| qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait) |
| { |
| scsi_qla_host_t *vha = cmd_sp->vha; |
| struct srb_iocb *abt_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| sp = qla2xxx_get_qpair_sp(cmd_sp->qpair, cmd_sp->fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| abt_iocb = &sp->u.iocb_cmd; |
| sp->type = SRB_ABT_CMD; |
| sp->name = "abort"; |
| sp->qpair = cmd_sp->qpair; |
| if (wait) |
| sp->flags = SRB_WAKEUP_ON_COMP; |
| |
| abt_iocb->timeout = qla24xx_abort_iocb_timeout; |
| init_completion(&abt_iocb->u.abt.comp); |
| /* FW can send 2 x ABTS's timeout/20s */ |
| qla2x00_init_timer(sp, 42); |
| |
| abt_iocb->u.abt.cmd_hndl = cmd_sp->handle; |
| abt_iocb->u.abt.req_que_no = cpu_to_le16(cmd_sp->qpair->req->id); |
| |
| sp->done = qla24xx_abort_sp_done; |
| |
| ql_dbg(ql_dbg_async, vha, 0x507c, |
| "Abort command issued - hdl=%x, type=%x\n", |
| cmd_sp->handle, cmd_sp->type); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| if (wait) { |
| wait_for_completion(&abt_iocb->u.abt.comp); |
| rval = abt_iocb->u.abt.comp_status == CS_COMPLETE ? |
| QLA_SUCCESS : QLA_FUNCTION_FAILED; |
| } else { |
| goto done; |
| } |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| return rval; |
| } |
| |
| int |
| qla24xx_async_abort_command(srb_t *sp) |
| { |
| unsigned long flags = 0; |
| |
| uint32_t handle; |
| fc_port_t *fcport = sp->fcport; |
| struct scsi_qla_host *vha = fcport->vha; |
| struct qla_hw_data *ha = vha->hw; |
| struct req_que *req = vha->req; |
| |
| if (vha->flags.qpairs_available && sp->qpair) |
| req = sp->qpair->req; |
| |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| for (handle = 1; handle < req->num_outstanding_cmds; handle++) { |
| if (req->outstanding_cmds[handle] == sp) |
| break; |
| } |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| if (handle == req->num_outstanding_cmds) { |
| /* Command not found. */ |
| return QLA_FUNCTION_FAILED; |
| } |
| if (sp->type == SRB_FXIOCB_DCMD) |
| return qlafx00_fx_disc(vha, &vha->hw->mr.fcport, |
| FXDISC_ABORT_IOCTL); |
| |
| return qla24xx_async_abort_cmd(sp, true); |
| } |
| |
| static void |
| qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea) |
| { |
| switch (ea->data[0]) { |
| case MBS_COMMAND_COMPLETE: |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| |
| ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset; |
| ea->fcport->logout_on_delete = 1; |
| qla24xx_post_gpdb_work(vha, ea->fcport, 0); |
| break; |
| default: |
| if ((ea->iop[0] == LSC_SCODE_ELS_REJECT) && |
| (ea->iop[1] == 0x50000)) { /* reson 5=busy expl:0x0 */ |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| ea->fcport->fw_login_state = DSC_LS_PLOGI_COMP; |
| break; |
| } |
| |
| if (ea->fcport->n2n_flag) { |
| ql_dbg(ql_dbg_disc, vha, 0x2118, |
| "%s %d %8phC post fc4 prli\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| ea->fcport->fc4f_nvme = 0; |
| ea->fcport->n2n_flag = 0; |
| qla24xx_post_prli_work(vha, ea->fcport); |
| } |
| ql_dbg(ql_dbg_disc, vha, 0x2119, |
| "%s %d %8phC unhandle event of %x\n", |
| __func__, __LINE__, ea->fcport->port_name, ea->data[0]); |
| break; |
| } |
| } |
| |
| static void |
| qla24xx_handle_plogi_done_event(struct scsi_qla_host *vha, struct event_arg *ea) |
| { |
| port_id_t cid; /* conflict Nport id */ |
| u16 lid; |
| struct fc_port *conflict_fcport; |
| unsigned long flags; |
| struct fc_port *fcport = ea->fcport; |
| |
| ql_dbg(ql_dbg_disc, vha, 0xffff, |
| "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d data %x|%x iop %x|%x\n", |
| __func__, fcport->port_name, fcport->disc_state, |
| fcport->fw_login_state, ea->rc, ea->sp->gen2, fcport->login_gen, |
| ea->sp->gen2, fcport->rscn_gen|ea->sp->gen1, |
| ea->data[0], ea->data[1], ea->iop[0], ea->iop[1]); |
| |
| if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND)) { |
| ql_dbg(ql_dbg_disc, vha, 0x20ea, |
| "%s %d %8phC Remote is trying to login\n", |
| __func__, __LINE__, fcport->port_name); |
| return; |
| } |
| |
| if ((fcport->disc_state == DSC_DELETE_PEND) || |
| (fcport->disc_state == DSC_DELETED)) { |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } |
| |
| if (ea->sp->gen2 != fcport->login_gen) { |
| /* target side must have changed it. */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed\n", |
| __func__, fcport->port_name); |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } else if (ea->sp->gen1 != fcport->rscn_gen) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d4, "%s %d %8phC post gidpn\n", |
| __func__, __LINE__, fcport->port_name); |
| qla24xx_post_gidpn_work(vha, fcport); |
| return; |
| } |
| |
| switch (ea->data[0]) { |
| case MBS_COMMAND_COMPLETE: |
| /* |
| * Driver must validate login state - If PRLI not complete, |
| * force a relogin attempt via implicit LOGO, PLOGI, and PRLI |
| * requests. |
| */ |
| if (ea->fcport->fc4f_nvme) { |
| ql_dbg(ql_dbg_disc, vha, 0x2117, |
| "%s %d %8phC post prli\n", |
| __func__, __LINE__, ea->fcport->port_name); |
| qla24xx_post_prli_work(vha, ea->fcport); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20ea, |
| "%s %d %8phC LoopID 0x%x in use with %06x. post gnl\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->loop_id, ea->fcport->d_id.b24); |
| |
| set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset; |
| ea->fcport->logout_on_delete = 1; |
| ea->fcport->send_els_logo = 0; |
| ea->fcport->fw_login_state = DSC_LS_PRLI_COMP; |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| qla24xx_post_gpdb_work(vha, ea->fcport, 0); |
| } |
| break; |
| case MBS_COMMAND_ERROR: |
| ql_dbg(ql_dbg_disc, vha, 0x20eb, "%s %d %8phC cmd error %x\n", |
| __func__, __LINE__, ea->fcport->port_name, ea->data[1]); |
| |
| ea->fcport->flags &= ~FCF_ASYNC_SENT; |
| ea->fcport->disc_state = DSC_LOGIN_FAILED; |
| if (ea->data[1] & QLA_LOGIO_LOGIN_RETRIED) |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| else |
| qla2x00_mark_device_lost(vha, ea->fcport, 1, 0); |
| break; |
| case MBS_LOOP_ID_USED: |
| /* data[1] = IO PARAM 1 = nport ID */ |
| cid.b.domain = (ea->iop[1] >> 16) & 0xff; |
| cid.b.area = (ea->iop[1] >> 8) & 0xff; |
| cid.b.al_pa = ea->iop[1] & 0xff; |
| cid.b.rsvd_1 = 0; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20ec, |
| "%s %d %8phC lid %#x in use with pid %06x post gnl\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->loop_id, cid.b24); |
| |
| set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); |
| ea->fcport->loop_id = FC_NO_LOOP_ID; |
| qla24xx_post_gnl_work(vha, ea->fcport); |
| break; |
| case MBS_PORT_ID_USED: |
| lid = ea->iop[1] & 0xffff; |
| qlt_find_sess_invalidate_other(vha, |
| wwn_to_u64(ea->fcport->port_name), |
| ea->fcport->d_id, lid, &conflict_fcport); |
| |
| if (conflict_fcport) { |
| /* |
| * Another fcport share the same loop_id/nport id. |
| * Conflict fcport needs to finish cleanup before this |
| * fcport can proceed to login. |
| */ |
| conflict_fcport->conflict = ea->fcport; |
| ea->fcport->login_pause = 1; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20ed, |
| "%s %d %8phC NPortId %06x inuse with loopid 0x%x. post gidpn\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->d_id.b24, lid); |
| qla2x00_clear_loop_id(ea->fcport); |
| qla24xx_post_gidpn_work(vha, ea->fcport); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20ed, |
| "%s %d %8phC NPortId %06x inuse with loopid 0x%x. sched delete\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->d_id.b24, lid); |
| |
| qla2x00_clear_loop_id(ea->fcport); |
| set_bit(lid, vha->hw->loop_id_map); |
| ea->fcport->loop_id = lid; |
| ea->fcport->keep_nport_handle = 0; |
| qlt_schedule_sess_for_deletion(ea->fcport); |
| } |
| break; |
| } |
| return; |
| } |
| |
| void |
| qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| qlt_logo_completion_handler(fcport, data[0]); |
| fcport->login_gen++; |
| fcport->flags &= ~FCF_ASYNC_ACTIVE; |
| return; |
| } |
| |
| void |
| qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); |
| if (data[0] == MBS_COMMAND_COMPLETE) { |
| qla2x00_update_fcport(vha, fcport); |
| |
| return; |
| } |
| |
| /* Retry login. */ |
| if (data[1] & QLA_LOGIO_LOGIN_RETRIED) |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| else |
| qla2x00_mark_device_lost(vha, fcport, 1, 0); |
| |
| return; |
| } |
| |
| /****************************************************************************/ |
| /* QLogic ISP2x00 Hardware Support Functions. */ |
| /****************************************************************************/ |
| |
| static 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; |
| uint16_t config[4]; |
| |
| 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); |
| |
| if (ha->flags.nic_core_reset_owner) { |
| memset(config, 0, sizeof(config)); |
| if (!qla81xx_get_port_config(vha, config)) |
| qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, |
| QLA8XXX_DEV_READY); |
| } |
| |
| rval = qla83xx_idc_state_handler(vha); |
| |
| exit: |
| qla83xx_idc_unlock(vha, 0); |
| |
| return rval; |
| } |
| |
| /* |
| * qla2x00_initialize_adapter |
| * Initialize board. |
| * |
| * Input: |
| * ha = adapter block pointer. |
| * |
| * Returns: |
| * 0 = success |
| */ |
| int |
| qla2x00_initialize_adapter(scsi_qla_host_t *vha) |
| { |
| int rval; |
| struct qla_hw_data *ha = vha->hw; |
| struct req_que *req = ha->req_q_map[0]; |
| |
| memset(&vha->qla_stats, 0, sizeof(vha->qla_stats)); |
| memset(&vha->fc_host_stat, 0, sizeof(vha->fc_host_stat)); |
| |
| /* Clear adapter flags. */ |
| vha->flags.online = 0; |
| ha->flags.chip_reset_done = 0; |
| vha->flags.reset_active = 0; |
| ha->flags.pci_channel_io_perm_failure = 0; |
| ha->flags.eeh_busy = 0; |
| vha->qla_stats.jiffies_at_last_reset = get_jiffies_64(); |
| atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); |
| atomic_set(&vha->loop_state, LOOP_DOWN); |
| vha->device_flags = DFLG_NO_CABLE; |
| vha->dpc_flags = 0; |
| vha->flags.management_server_logged_in = 0; |
| vha->marker_needed = 0; |
| ha->isp_abort_cnt = 0; |
| ha->beacon_blink_led = 0; |
| |
| set_bit(0, ha->req_qid_map); |
| set_bit(0, ha->rsp_qid_map); |
| |
| ql_dbg(ql_dbg_init, vha, 0x0040, |
| "Configuring PCI space...\n"); |
| rval = ha->isp_ops->pci_config(vha); |
| if (rval) { |
| ql_log(ql_log_warn, vha, 0x0044, |
| "Unable to configure PCI space.\n"); |
| return (rval); |
| } |
| |
| ha->isp_ops->reset_chip(vha); |
| |
| rval = qla2xxx_get_flash_info(vha); |
| if (rval) { |
| ql_log(ql_log_fatal, vha, 0x004f, |
| "Unable to validate FLASH data.\n"); |
| return rval; |
| } |
| |
| if (IS_QLA8044(ha)) { |
| qla8044_read_reset_template(vha); |
| |
| /* NOTE: If ql2xdontresethba==1, set IDC_CTRL DONTRESET_BIT0. |
| * If DONRESET_BIT0 is set, drivers should not set dev_state |
| * to NEED_RESET. But if NEED_RESET is set, drivers should |
| * should honor the reset. */ |
| if (ql2xdontresethba == 1) |
| qla8044_set_idc_dontreset(vha); |
| } |
| |
| ha->isp_ops->get_flash_version(vha, req->ring); |
| ql_dbg(ql_dbg_init, vha, 0x0061, |
| "Configure NVRAM parameters...\n"); |
| |
| ha->isp_ops->nvram_config(vha); |
| |
| if (ha->flags.disable_serdes) { |
| /* Mask HBA via NVRAM settings? */ |
| ql_log(ql_log_info, vha, 0x0077, |
| "Masking HBA WWPN %8phN (via NVRAM).\n", vha->port_name); |
| return QLA_FUNCTION_FAILED; |
| } |
| |
| ql_dbg(ql_dbg_init, vha, 0x0078, |
| "Verifying loaded RISC code...\n"); |
| |
| if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) { |
| rval = ha->isp_ops->chip_diag(vha); |
| if (rval) |
| return (rval); |
| rval = qla2x00_setup_chip(vha); |
| if (rval) |
| return (rval); |
| } |
| |
| if (IS_QLA84XX(ha)) { |
| ha->cs84xx = qla84xx_get_chip(vha); |
| if (!ha->cs84xx) { |
| ql_log(ql_log_warn, vha, 0x00d0, |
| "Unable to configure ISP84XX.\n"); |
| return QLA_FUNCTION_FAILED; |
| } |
| } |
| |
| if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha)) |
| rval = qla2x00_init_rings(vha); |
| |
| ha->flags.chip_reset_done = 1; |
| |
| if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) { |
| /* Issue verify 84xx FW IOCB to complete 84xx initialization */ |
| rval = qla84xx_init_chip(vha); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x00d4, |
| "Unable to initialize ISP84XX.\n"); |
| qla84xx_put_chip(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); |
| |
| if (IS_P3P_TYPE(ha)) |
| qla82xx_set_driver_version(vha, QLA2XXX_VERSION); |
| else |
| qla25xx_set_driver_version(vha, QLA2XXX_VERSION); |
| |
| return (rval); |
| } |
| |
| /** |
| * qla2100_pci_config() - Setup ISP21xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla2100_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| unsigned long flags; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| pci_disable_rom(ha->pdev); |
| |
| /* Get PCI bus information. */ |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| ha->pci_attr = RD_REG_WORD(®->ctrl_status); |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| return QLA_SUCCESS; |
| } |
| |
| /** |
| * qla2300_pci_config() - Setup ISP23xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla2300_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| unsigned long flags = 0; |
| uint32_t cnt; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| |
| if (IS_QLA2322(ha) || IS_QLA6322(ha)) |
| w &= ~PCI_COMMAND_INTX_DISABLE; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| /* |
| * If this is a 2300 card and not 2312, reset the |
| * COMMAND_INVALIDATE due to a bug in the 2300. Unfortunately, |
| * the 2310 also reports itself as a 2300 so we need to get the |
| * fb revision level -- a 6 indicates it really is a 2300 and |
| * not a 2310. |
| */ |
| if (IS_QLA2300(ha)) { |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| |
| /* Pause RISC. */ |
| WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); |
| for (cnt = 0; cnt < 30000; cnt++) { |
| if ((RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) != 0) |
| break; |
| |
| udelay(10); |
| } |
| |
| /* Select FPM registers. */ |
| WRT_REG_WORD(®->ctrl_status, 0x20); |
| RD_REG_WORD(®->ctrl_status); |
| |
| /* Get the fb rev level */ |
| ha->fb_rev = RD_FB_CMD_REG(ha, reg); |
| |
| if (ha->fb_rev == FPM_2300) |
| pci_clear_mwi(ha->pdev); |
| |
| /* Deselect FPM registers. */ |
| WRT_REG_WORD(®->ctrl_status, 0x0); |
| RD_REG_WORD(®->ctrl_status); |
| |
| /* Release RISC module. */ |
| WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); |
| for (cnt = 0; cnt < 30000; cnt++) { |
| if ((RD_REG_WORD(®->hccr) & HCCR_RISC_PAUSE) == 0) |
| break; |
| |
| udelay(10); |
| } |
| |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| } |
| |
| pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); |
| |
| pci_disable_rom(ha->pdev); |
| |
| /* Get PCI bus information. */ |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| ha->pci_attr = RD_REG_WORD(®->ctrl_status); |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| return QLA_SUCCESS; |
| } |
| |
| /** |
| * qla24xx_pci_config() - Setup ISP24xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla24xx_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| unsigned long flags = 0; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| w &= ~PCI_COMMAND_INTX_DISABLE; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); |
| |
| /* PCI-X -- adjust Maximum Memory Read Byte Count (2048). */ |
| if (pci_find_capability(ha->pdev, PCI_CAP_ID_PCIX)) |
| pcix_set_mmrbc(ha->pdev, 2048); |
| |
| /* PCIe -- adjust Maximum Read Request Size (2048). */ |
| if (pci_is_pcie(ha->pdev)) |
| pcie_set_readrq(ha->pdev, 4096); |
| |
| pci_disable_rom(ha->pdev); |
| |
| ha->chip_revision = ha->pdev->revision; |
| |
| /* Get PCI bus information. */ |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| ha->pci_attr = RD_REG_DWORD(®->ctrl_status); |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| return QLA_SUCCESS; |
| } |
| |
| /** |
| * qla25xx_pci_config() - Setup ISP25xx PCI configuration registers. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla25xx_pci_config(scsi_qla_host_t *vha) |
| { |
| uint16_t w; |
| struct qla_hw_data *ha = vha->hw; |
| |
| pci_set_master(ha->pdev); |
| pci_try_set_mwi(ha->pdev); |
| |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &w); |
| w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); |
| w &= ~PCI_COMMAND_INTX_DISABLE; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, w); |
| |
| /* PCIe -- adjust Maximum Read Request Size (2048). */ |
| if (pci_is_pcie(ha->pdev)) |
| pcie_set_readrq(ha->pdev, 4096); |
| |
| pci_disable_rom(ha->pdev); |
| |
| ha->chip_revision = ha->pdev->revision; |
| |
| return QLA_SUCCESS; |
| } |
| |
| /** |
| * qla2x00_isp_firmware() - Choose firmware image. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| static int |
| qla2x00_isp_firmware(scsi_qla_host_t *vha) |
| { |
| int rval; |
| uint16_t loop_id, topo, sw_cap; |
| uint8_t domain, area, al_pa; |
| struct qla_hw_data *ha = vha->hw; |
| |
| /* Assume loading risc code */ |
| rval = QLA_FUNCTION_FAILED; |
| |
| if (ha->flags.disable_risc_code_load) { |
| ql_log(ql_log_info, vha, 0x0079, "RISC CODE NOT loaded.\n"); |
| |
| /* Verify checksum of loaded RISC code. */ |
| rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address); |
| if (rval == QLA_SUCCESS) { |
| /* And, verify we are not in ROM code. */ |
| rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa, |
| &area, &domain, &topo, &sw_cap); |
| } |
| } |
| |
| if (rval) |
| ql_dbg(ql_dbg_init, vha, 0x007a, |
| "**** Load RISC code ****.\n"); |
| |
| return (rval); |
| } |
| |
| /** |
| * qla2x00_reset_chip() - Reset ISP chip. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| void |
| qla2x00_reset_chip(scsi_qla_host_t *vha) |
| { |
| unsigned long flags = 0; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| uint32_t cnt; |
| uint16_t cmd; |
| |
| if (unlikely(pci_channel_offline(ha->pdev))) |
| return; |
| |
| ha->isp_ops->disable_intrs(ha); |
| |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| |
| /* Turn off master enable */ |
| cmd = 0; |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &cmd); |
| cmd &= ~PCI_COMMAND_MASTER; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, cmd); |
| |
| if (!IS_QLA2100(ha)) { |
| /* Pause RISC. */ |
| WRT_REG_WORD(®->hccr, HCCR_PAUSE_RISC); |
| if (IS_QLA2200(ha) || IS_QLA2300(ha)) { |
| for (cnt = 0; cnt < 30000; cnt++) { |
| if ((RD_REG_WORD(®->hccr) & |
| HCCR_RISC_PAUSE) != 0) |
| break; |
| udelay(100); |
| } |
| } else { |
| RD_REG_WORD(®->hccr); /* PCI Posting. */ |
| udelay(10); |
| } |
| |
| /* Select FPM registers. */ |
| WRT_REG_WORD(®->ctrl_status, 0x20); |
| RD_REG_WORD(®->ctrl_status); /* PCI Posting. */ |
| |
| /* FPM Soft Reset. */ |
| WRT_REG_WORD(®->fpm_diag_config, 0x100); |
| RD_REG_WORD(®->fpm_diag_config); /* PCI Posting. */ |
| |
| /* Toggle Fpm Reset. */ |
| if (!IS_QLA2200(ha)) { |
| WRT_REG_WORD(®->fpm_diag_config, 0x0); |
| RD_REG_WORD(®->fpm_diag_config); /* PCI Posting. */ |
| } |
| |
| /* Select frame buffer registers. */ |
| WRT_REG_WORD(®->ctrl_status, 0x10); |
| RD_REG_WORD(®->ctrl_status); /* PCI Posting. */ |
| |
| /* Reset frame buffer FIFOs. */ |
| if (IS_QLA2200(ha)) { |
| WRT_FB_CMD_REG(ha, reg, 0xa000); |
| RD_FB_CMD_REG(ha, reg); /* PCI Posting. */ |
| } else { |
| WRT_FB_CMD_REG(ha, reg, 0x00fc); |
| |
| /* Read back fb_cmd until zero or 3 seconds max */ |
| for (cnt = 0; cnt < 3000; cnt++) { |
| if ((RD_FB_CMD_REG(ha, reg) & 0xff) == 0) |
| break; |
| udelay(100); |
| } |
| } |
| |
| /* Select RISC module registers. */ |
| WRT_REG_WORD(®->ctrl_status, 0); |
| RD_REG_WORD(®->ctrl_status); /* PCI Posting. */ |
| |
| /* Reset RISC processor. */ |
| WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); |
| RD_REG_WORD(®->hccr); /* PCI Posting. */ |
| |
| /* Release RISC processor. */ |
| WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); |
| RD_REG_WORD(®->hccr); /* PCI Posting. */ |
| } |
| |
| WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); |
| WRT_REG_WORD(®->hccr, HCCR_CLR_HOST_INT); |
| |
| /* Reset ISP chip. */ |
| WRT_REG_WORD(®->ctrl_status, CSR_ISP_SOFT_RESET); |
| |
| /* Wait for RISC to recover from reset. */ |
| if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) { |
| /* |
| * It is necessary to for a delay here since the card doesn't |
| * respond to PCI reads during a reset. On some architectures |
| * this will result in an MCA. |
| */ |
| udelay(20); |
| for (cnt = 30000; cnt; cnt--) { |
| if ((RD_REG_WORD(®->ctrl_status) & |
| CSR_ISP_SOFT_RESET) == 0) |
| break; |
| udelay(100); |
| } |
| } else |
| udelay(10); |
| |
| /* Reset RISC processor. */ |
| WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); |
| |
| WRT_REG_WORD(®->semaphore, 0); |
| |
| /* Release RISC processor. */ |
| WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); |
| RD_REG_WORD(®->hccr); /* PCI Posting. */ |
| |
| if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) { |
| for (cnt = 0; cnt < 30000; cnt++) { |
| if (RD_MAILBOX_REG(ha, reg, 0) != MBS_BUSY) |
| break; |
| |
| udelay(100); |
| } |
| } else |
| udelay(100); |
| |
| /* Turn on master enable */ |
| cmd |= PCI_COMMAND_MASTER; |
| pci_write_config_word(ha->pdev, PCI_COMMAND, cmd); |
| |
| /* Disable RISC pause on FPM parity error. */ |
| if (!IS_QLA2100(ha)) { |
| WRT_REG_WORD(®->hccr, HCCR_DISABLE_PARITY_PAUSE); |
| RD_REG_WORD(®->hccr); /* PCI Posting. */ |
| } |
| |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| } |
| |
| /** |
| * qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| static int |
| qla81xx_reset_mpi(scsi_qla_host_t *vha) |
| { |
| uint16_t mb[4] = {0x1010, 0, 1, 0}; |
| |
| if (!IS_QLA81XX(vha->hw)) |
| return QLA_SUCCESS; |
| |
| return qla81xx_write_mpi_register(vha, mb); |
| } |
| |
| /** |
| * qla24xx_reset_risc() - Perform full reset of ISP24xx RISC. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| static inline int |
| qla24xx_reset_risc(scsi_qla_host_t *vha) |
| { |
| unsigned long flags = 0; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; |
| uint32_t cnt; |
| uint16_t wd; |
| static int abts_cnt; /* ISP abort retry counts */ |
| int rval = QLA_SUCCESS; |
| |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| |
| /* Reset RISC. */ |
| WRT_REG_DWORD(®->ctrl_status, CSRX_DMA_SHUTDOWN|MWB_4096_BYTES); |
| for (cnt = 0; cnt < 30000; cnt++) { |
| if ((RD_REG_DWORD(®->ctrl_status) & CSRX_DMA_ACTIVE) == 0) |
| break; |
| |
| udelay(10); |
| } |
| |
| if (!(RD_REG_DWORD(®->ctrl_status) & CSRX_DMA_ACTIVE)) |
| set_bit(DMA_SHUTDOWN_CMPL, &ha->fw_dump_cap_flags); |
| |
| ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x017e, |
| "HCCR: 0x%x, Control Status %x, DMA active status:0x%x\n", |
| RD_REG_DWORD(®->hccr), |
| RD_REG_DWORD(®->ctrl_status), |
| (RD_REG_DWORD(®->ctrl_status) & CSRX_DMA_ACTIVE)); |
| |
| WRT_REG_DWORD(®->ctrl_status, |
| CSRX_ISP_SOFT_RESET|CSRX_DMA_SHUTDOWN|MWB_4096_BYTES); |
| pci_read_config_word(ha->pdev, PCI_COMMAND, &wd); |
| |
| udelay(100); |
| |
| /* Wait for firmware to complete NVRAM accesses. */ |
| RD_REG_WORD(®->mailbox0); |
| for (cnt = 10000; RD_REG_WORD(®->mailbox0) != 0 && |
| rval == QLA_SUCCESS; cnt--) { |
| barrier(); |
| if (cnt) |
| udelay(5); |
| else |
| rval = QLA_FUNCTION_TIMEOUT; |
| } |
| |
| if (rval == QLA_SUCCESS) |
| set_bit(ISP_MBX_RDY, &ha->fw_dump_cap_flags); |
| |
| ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x017f, |
| "HCCR: 0x%x, MailBox0 Status 0x%x\n", |
| RD_REG_DWORD(®->hccr), |
| RD_REG_DWORD(®->mailbox0)); |
| |
| /* Wait for soft-reset to complete. */ |
| RD_REG_DWORD(®->ctrl_status); |
| for (cnt = 0; cnt < 60; cnt++) { |
| barrier(); |
| if ((RD_REG_DWORD(®->ctrl_status) & |
| CSRX_ISP_SOFT_RESET) == 0) |
| break; |
| |
| udelay(5); |
| } |
| if (!(RD_REG_DWORD(®->ctrl_status) & CSRX_ISP_SOFT_RESET)) |
| set_bit(ISP_SOFT_RESET_CMPL, &ha->fw_dump_cap_flags); |
| |
| ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015d, |
| "HCCR: 0x%x, Soft Reset status: 0x%x\n", |
| RD_REG_DWORD(®->hccr), |
| RD_REG_DWORD(®->ctrl_status)); |
| |
| /* If required, do an MPI FW reset now */ |
| if (test_and_clear_bit(MPI_RESET_NEEDED, &vha->dpc_flags)) { |
| if (qla81xx_reset_mpi(vha) != QLA_SUCCESS) { |
| if (++abts_cnt < 5) { |
| set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); |
| set_bit(MPI_RESET_NEEDED, &vha->dpc_flags); |
| } else { |
| /* |
| * We exhausted the ISP abort retries. We have to |
| * set the board offline. |
| */ |
| abts_cnt = 0; |
| vha->flags.online = 0; |
| } |
| } |
| } |
| |
| WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET); |
| RD_REG_DWORD(®->hccr); |
| |
| WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); |
| RD_REG_DWORD(®->hccr); |
| |
| WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); |
| RD_REG_DWORD(®->hccr); |
| |
| RD_REG_WORD(®->mailbox0); |
| for (cnt = 60; RD_REG_WORD(®->mailbox0) != 0 && |
| rval == QLA_SUCCESS; cnt--) { |
| barrier(); |
| if (cnt) |
| udelay(5); |
| else |
| rval = QLA_FUNCTION_TIMEOUT; |
| } |
| if (rval == QLA_SUCCESS) |
| set_bit(RISC_RDY_AFT_RESET, &ha->fw_dump_cap_flags); |
| |
| ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015e, |
| "Host Risc 0x%x, mailbox0 0x%x\n", |
| RD_REG_DWORD(®->hccr), |
| RD_REG_WORD(®->mailbox0)); |
| |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015f, |
| "Driver in %s mode\n", |
| IS_NOPOLLING_TYPE(ha) ? "Interrupt" : "Polling"); |
| |
| if (IS_NOPOLLING_TYPE(ha)) |
| ha->isp_ops->enable_intrs(ha); |
| |
| return rval; |
| } |
| |
| static void |
| qla25xx_read_risc_sema_reg(scsi_qla_host_t *vha, uint32_t *data) |
| { |
| struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24; |
| |
| WRT_REG_DWORD(®->iobase_addr, RISC_REGISTER_BASE_OFFSET); |
| *data = RD_REG_DWORD(®->iobase_window + RISC_REGISTER_WINDOW_OFFET); |
| |
| } |
| |
| static void |
| qla25xx_write_risc_sema_reg(scsi_qla_host_t *vha, uint32_t data) |
| { |
| struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24; |
| |
| WRT_REG_DWORD(®->iobase_addr, RISC_REGISTER_BASE_OFFSET); |
| WRT_REG_DWORD(®->iobase_window + RISC_REGISTER_WINDOW_OFFET, data); |
| } |
| |
| static void |
| qla25xx_manipulate_risc_semaphore(scsi_qla_host_t *vha) |
| { |
| uint32_t wd32 = 0; |
| uint delta_msec = 100; |
| uint elapsed_msec = 0; |
| uint timeout_msec; |
| ulong n; |
| |
| if (vha->hw->pdev->subsystem_device != 0x0175 && |
| vha->hw->pdev->subsystem_device != 0x0240) |
| return; |
| |
| WRT_REG_DWORD(&vha->hw->iobase->isp24.hccr, HCCRX_SET_RISC_PAUSE); |
| udelay(100); |
| |
| attempt: |
| timeout_msec = TIMEOUT_SEMAPHORE; |
| n = timeout_msec / delta_msec; |
| while (n--) { |
| qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_SET); |
| qla25xx_read_risc_sema_reg(vha, &wd32); |
| if (wd32 & RISC_SEMAPHORE) |
| break; |
| msleep(delta_msec); |
| elapsed_msec += delta_msec; |
| if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED) |
| goto force; |
| } |
| |
| if (!(wd32 & RISC_SEMAPHORE)) |
| goto force; |
| |
| if (!(wd32 & RISC_SEMAPHORE_FORCE)) |
| goto acquired; |
| |
| qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_CLR); |
| timeout_msec = TIMEOUT_SEMAPHORE_FORCE; |
| n = timeout_msec / delta_msec; |
| while (n--) { |
| qla25xx_read_risc_sema_reg(vha, &wd32); |
| if (!(wd32 & RISC_SEMAPHORE_FORCE)) |
| break; |
| msleep(delta_msec); |
| elapsed_msec += delta_msec; |
| if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED) |
| goto force; |
| } |
| |
| if (wd32 & RISC_SEMAPHORE_FORCE) |
| qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_CLR); |
| |
| goto attempt; |
| |
| force: |
| qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_SET); |
| |
| acquired: |
| return; |
| } |
| |
| /** |
| * qla24xx_reset_chip() - Reset ISP24xx chip. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| void |
| qla24xx_reset_chip(scsi_qla_host_t *vha) |
| { |
| struct qla_hw_data *ha = vha->hw; |
| |
| if (pci_channel_offline(ha->pdev) && |
| ha->flags.pci_channel_io_perm_failure) { |
| return; |
| } |
| |
| ha->isp_ops->disable_intrs(ha); |
| |
| qla25xx_manipulate_risc_semaphore(vha); |
| |
| /* Perform RISC reset. */ |
| qla24xx_reset_risc(vha); |
| } |
| |
| /** |
| * qla2x00_chip_diag() - Test chip for proper operation. |
| * @vha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla2x00_chip_diag(scsi_qla_host_t *vha) |
| { |
| int rval; |
| struct qla_hw_data *ha = vha->hw; |
| struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; |
| unsigned long flags = 0; |
| uint16_t data; |
| uint32_t cnt; |
| uint16_t mb[5]; |
| struct req_que *req = ha->req_q_map[0]; |
| |
| /* Assume a failed state */ |
| rval = QLA_FUNCTION_FAILED; |
| |
| ql_dbg(ql_dbg_init, vha, 0x007b, "Testing device at %p.\n", |
| ®->flash_address); |
| |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| |
| /* Reset ISP chip. */ |
| WRT_REG_WORD(®->ctrl_status, CSR_ISP_SOFT_RESET); |
| |
| /* |
| * We need to have a delay here since the card will not respond while |
| * in reset causing an MCA on some architectures. |
| */ |
| udelay(20); |
| data = qla2x00_debounce_register(®->ctrl_status); |
| for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) { |
| udelay(5); |
| data = RD_REG_WORD(®->ctrl_status); |
| barrier(); |
| } |
| |
| if (!cnt) |
| goto chip_diag_failed; |
| |
| ql_dbg(ql_dbg_init, vha, 0x007c, |
| "Reset register cleared by chip reset.\n"); |
| |
| /* Reset RISC processor. */ |
| WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); |
| WRT_REG_WORD(®->hccr, HCCR_RELEASE_RISC); |
| |
| /* Workaround for QLA2312 PCI parity error */ |
| if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) { |
| data = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 0)); |
| for (cnt = 6000000; cnt && (data == MBS_BUSY); cnt--) { |
| udelay(5); |
| data = RD_MAILBOX_REG(ha, reg, 0); |
| barrier(); |
| } |
| } else |
| udelay(10); |
| |
| if (!cnt) |
| goto chip_diag_failed; |
| |
| /* Check product ID of chip */ |
| ql_dbg(ql_dbg_init, vha, 0x007d, "Checking product ID of chip.\n"); |
| |
| mb[1] = RD_MAILBOX_REG(ha, reg, 1); |
| mb[2] = RD_MAILBOX_REG(ha, reg, 2); |
| mb[3] = RD_MAILBOX_REG(ha, reg, 3); |
| mb[4] = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 4)); |
| if (mb[1] != PROD_ID_1 || (mb[2] != PROD_ID_2 && mb[2] != PROD_ID_2a) || |
| mb[3] != PROD_ID_3) { |
|