| /* |
| * 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 *); |
| |
| /* SRB Extensions ---------------------------------------------------------- */ |
| |
| void |
| qla2x00_sp_timeout(unsigned long __data) |
| { |
| srb_t *sp = (srb_t *)__data; |
| struct srb_iocb *iocb; |
| scsi_qla_host_t *vha = sp->vha; |
| struct req_que *req; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&vha->hw->hardware_lock, flags); |
| req = vha->hw->req_q_map[0]; |
| req->outstanding_cmds[sp->handle] = NULL; |
| iocb = &sp->u.iocb_cmd; |
| iocb->timeout(sp); |
| sp->free(sp); |
| spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); |
| } |
| |
| 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; |
| struct event_arg ea; |
| |
| 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; |
| |
| switch (sp->type) { |
| case SRB_LOGIN_CMD: |
| /* 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; |
| 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.sp = sp; |
| qla24xx_handle_plogi_done_event(fcport->vha, &ea); |
| break; |
| case SRB_LOGOUT_CMD: |
| qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT); |
| break; |
| case SRB_CT_PTHRU_CMD: |
| case SRB_MB_IOCB: |
| case SRB_NACK_PLOGI: |
| case SRB_NACK_PRLI: |
| case SRB_NACK_LOGO: |
| 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; |
| 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); |
| } |
| |
| 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) |
| goto done; |
| |
| if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || |
| (fcport->fw_login_state == DSC_LS_PLOGI_COMP) || |
| (fcport->fw_login_state == DSC_LS_PRLI_PEND)) |
| goto done; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->logout_completed = 0; |
| |
| sp->type = SRB_LOGIN_CMD; |
| sp->name = "login"; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| sp->done = qla2x00_async_login_sp_done; |
| lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; |
| |
| if (fcport->fc4f_nvme) |
| lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI; |
| |
| if (data[1] & QLA_LOGIO_LOGIN_RETRIED) |
| lio->u.logio.flags |= SRB_LOGIN_RETRIED; |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) { |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| fcport->flags |= FCF_LOGIN_NEEDED; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| goto done_free_sp; |
| } |
| |
| 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); |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| return rval; |
| } |
| |
| static void |
| qla2x00_async_logout_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct srb_iocb *lio = &sp->u.iocb_cmd; |
| |
| sp->fcport->flags &= ~FCF_ASYNC_SENT; |
| if (!test_bit(UNLOADING, &sp->vha->dpc_flags)) |
| qla2x00_post_async_logout_done_work(sp->vha, sp->fcport, |
| lio->u.logio.data); |
| 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; |
| |
| 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"; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| sp->done = qla2x00_async_logout_sp_done; |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| 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); |
| return rval; |
| |
| done_free_sp: |
| sp->free(sp); |
| done: |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| return rval; |
| } |
| |
| static void |
| qla2x00_async_adisc_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; |
| |
| if (!test_bit(UNLOADING, &vha->dpc_flags)) |
| qla2x00_post_async_adisc_done_work(sp->vha, sp->fcport, |
| lio->u.logio.data); |
| 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"; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| sp->done = qla2x00_async_adisc_sp_done; |
| if (data[1] & QLA_LOGIO_LOGIN_RETRIED) |
| lio->u.logio.flags |= SRB_LOGIN_RETRIED; |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x206f, |
| "Async-adisc - hdl=%x loopid=%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_SENT; |
| 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; |
| u8 opt = 0, current_login_state; |
| |
| fcport = ea->fcport; |
| |
| if (ea->rc) { /* rval */ |
| if (fcport->login_retry == 0) { |
| fcport->login_retry = vha->hw->login_retry_count; |
| 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 login %d|%d\n", |
| __func__, fcport->port_name, |
| fcport->last_login_gen, fcport->login_gen); |
| 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); |
| |
| if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE)) |
| continue; |
| |
| found = 1; |
| 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; |
| |
| loop_id = le16_to_cpu(e->nport_handle); |
| loop_id = (loop_id & 0x7fff); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e2, |
| "%s found %8phC CLS [%d|%d] ID[%02x%02x%02x|%02x%02x%02x] lid[%d|%d]\n", |
| __func__, fcport->port_name, |
| e->current_login_state, fcport->fw_login_state, |
| 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); |
| |
| if ((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, 1); |
| return; |
| } |
| |
| 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; |
| } |
| |
| if (fcport->fc4f_nvme) |
| current_login_state = e->current_login_state >> 4; |
| else |
| current_login_state = e->current_login_state & 0xf; |
| |
| switch (current_login_state) { |
| case DSC_LS_PRLI_COMP: |
| ql_dbg(ql_dbg_disc, vha, 0x20e4, |
| "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, fcport->port_name); |
| opt = PDO_FORCE_ADISC; |
| qla24xx_post_gpdb_work(vha, fcport, opt); |
| 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; |
| } |
| } |
| |
| if (!found) { |
| /* fw has no record of this port */ |
| if (fcport->loop_id == FC_NO_LOOP_ID) { |
| qla2x00_find_new_loop_id(vha, fcport); |
| fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; |
| } else { |
| 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); |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20e6, |
| "%s %d %8phC post del sess\n", |
| __func__, __LINE__, |
| conflict_fcport->port_name); |
| qlt_schedule_sess_for_deletion |
| (conflict_fcport, 1); |
| } |
| |
| if (fcport->loop_id == loop_id) { |
| /* FW already picked this loop id for another fcport */ |
| qla2x00_find_new_loop_id(vha, fcport); |
| } |
| } |
| } |
| qla24xx_fcport_handle_login(vha, fcport); |
| } |
| } /* 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; |
| |
| 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]); |
| |
| 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->hw->tgt.sess_lock, flags); |
| vha->gnl.sent = 0; |
| |
| 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); |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| ea.fcport = fcport; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| } |
| |
| 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) |
| goto done; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d9, |
| "Async-gnlist WWPN %8phC \n", fcport->port_name); |
| |
| spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->disc_state = DSC_GNL; |
| fcport->last_rscn_gen = fcport->rscn_gen; |
| fcport->last_login_gen = fcport->login_gen; |
| |
| list_add_tail(&fcport->gnl_entry, &vha->gnl.fcports); |
| if (vha->gnl.sent) { |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| rval = QLA_SUCCESS; |
| goto done; |
| } |
| vha->gnl.sent = 1; |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gnlist"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| |
| 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; |
| |
| mbx = &sp->u.iocb_cmd; |
| mbx->timeout = qla2x00_async_iocb_timeout; |
| |
| 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); |
| done: |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| 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; |
| 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; |
| struct port_database_24xx *pd; |
| fc_port_t *fcport = sp->fcport; |
| u16 *mb = sp->u.iocb_cmd.u.mbx.in_mb; |
| int rval = QLA_SUCCESS; |
| 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; |
| |
| if (res) { |
| rval = res; |
| goto gpd_error_out; |
| } |
| |
| pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in; |
| |
| rval = __qla24xx_parse_gpdb(vha, fcport, pd); |
| |
| gpd_error_out: |
| memset(&ea, 0, sizeof(ea)); |
| ea.event = FCME_GPDB_DONE; |
| ea.rc = rval; |
| ea.fcport = fcport; |
| ea.sp = sp; |
| |
| qla2x00_fcport_event_handler(vha, &ea); |
| |
| 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_PLOGI_COMP || |
| 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"; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| lio = &sp->u.iocb_cmd; |
| lio->timeout = qla2x00_async_iocb_timeout; |
| 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_ASYNC_SENT; |
| 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.\n", |
| fcport->port_name, sp->handle, fcport->loop_id, |
| fcport->d_id.b24, fcport->login_retry); |
| |
| 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; |
| 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) |
| goto done; |
| |
| fcport->flags |= FCF_ASYNC_SENT; |
| fcport->disc_state = DSC_GPDB; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| sp->type = SRB_MB_IOCB; |
| sp->name = "gpdb"; |
| sp->gen1 = fcport->rscn_gen; |
| sp->gen2 = fcport->login_gen; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); |
| |
| pd = dma_pool_alloc(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; |
| } |
| memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE)); |
| |
| 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 = &sp->u.iocb_cmd; |
| mbx->timeout = qla2x00_async_iocb_timeout; |
| mbx->u.mbx.in = (void *)pd; |
| mbx->u.mbx.in_dma = pd_dma; |
| |
| sp->done = qla24xx_async_gpdb_sp_done; |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20dc, |
| "Async-%s %8phC hndl %x opt %x\n", |
| sp->name, fcport->port_name, sp->handle, opt); |
| |
| return rval; |
| |
| done_free_sp: |
| if (pd) |
| dma_pool_free(ha->s_dma_pool, pd, pd_dma); |
| |
| sp->free(sp); |
| done: |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| qla24xx_post_gpdb_work(vha, fcport, opt); |
| return rval; |
| } |
| |
| static |
| void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| int rval = ea->rc; |
| fc_port_t *fcport = ea->fcport; |
| unsigned long flags; |
| |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d2, |
| "%s %8phC DS %d LS %d rval %d\n", __func__, fcport->port_name, |
| fcport->disc_state, fcport->fw_login_state, rval); |
| |
| if (ea->sp->gen2 != fcport->login_gen) { |
| /* target side must have changed it. */ |
| ql_dbg(ql_dbg_disc, vha, 0x20d3, |
| "%s %8phC generation changed rscn %d|%d login %d|%d \n", |
| __func__, fcport->port_name, fcport->last_rscn_gen, |
| fcport->rscn_gen, fcport->last_login_gen, |
| fcport->login_gen); |
| 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; |
| } |
| |
| if (rval != QLA_SUCCESS) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC post del sess\n", |
| __func__, __LINE__, fcport->port_name); |
| qlt_schedule_sess_for_deletion_lock(fcport); |
| return; |
| } |
| |
| 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; |
| |
| if (!IS_IIDMA_CAPABLE(vha->hw) || |
| !vha->hw->flags.gpsc_supported) { |
| ql_dbg(ql_dbg_disc, vha, 0x20d6, |
| "%s %d %8phC post upd_fcport fcp_cnt %d\n", |
| __func__, __LINE__, fcport->port_name, |
| vha->fcport_count); |
| |
| qla24xx_post_upd_fcport_work(vha, fcport); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20d7, |
| "%s %d %8phC post gpsc fcp_cnt %d\n", |
| __func__, __LINE__, fcport->port_name, |
| vha->fcport_count); |
| |
| qla24xx_post_gpsc_work(vha, fcport); |
| } |
| } |
| spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); |
| } /* gpdb event */ |
| |
| int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) |
| { |
| if (fcport->login_retry == 0) |
| return 0; |
| |
| if (fcport->scan_state != QLA_FCPORT_FOUND) |
| return 0; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20d8, |
| "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d|%d retry %d lid %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->last_login_gen, fcport->login_gen, fcport->login_retry, |
| fcport->loop_id); |
| |
| fcport->login_retry--; |
| |
| if ((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)) |
| 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: |
| 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_async_gnl(vha, fcport); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20bf, |
| "%s %d %8phC post login\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->disc_state = DSC_LOGIN_PEND; |
| qla2x00_post_async_login_work(vha, fcport, NULL); |
| } |
| break; |
| |
| case DSC_GNL: |
| 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; |
| } |
| |
| if (fcport->flags & FCF_FCP2_DEVICE) { |
| u8 opt = PDO_FORCE_ADISC; |
| |
| ql_dbg(ql_dbg_disc, vha, 0x20c9, |
| "%s %d %8phC post gpdb\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| fcport->disc_state = DSC_GPDB; |
| qla24xx_post_gpdb_work(vha, fcport, opt); |
| } else { |
| ql_dbg(ql_dbg_disc, vha, 0x20cf, |
| "%s %d %8phC post login\n", |
| __func__, __LINE__, fcport->port_name); |
| fcport->disc_state = DSC_LOGIN_PEND; |
| qla2x00_post_async_login_work(vha, fcport, NULL); |
| } |
| |
| break; |
| |
| case DSC_LOGIN_FAILED: |
| ql_dbg(ql_dbg_disc, vha, 0x20d0, |
| "%s %d %8phC post gidpn\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| 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 gpdb\n", |
| __func__, __LINE__, fcport->port_name); |
| |
| qla24xx_post_gpdb_work(vha, fcport, PDO_FORCE_ADISC); |
| 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_gidpn_work(fcport->vha, fcport); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, |
| u8 *port_name, void *pla) |
| { |
| 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; |
| memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); |
| |
| return qla2x00_post_work(vha, e); |
| } |
| |
| static |
| int qla24xx_handle_delete_done_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| |
| if (test_bit(UNLOADING, &vha->dpc_flags)) |
| return 0; |
| |
| switch (vha->host->active_mode) { |
| case MODE_INITIATOR: |
| case MODE_DUAL: |
| if (fcport->scan_state == QLA_FCPORT_FOUND) |
| qla24xx_fcport_handle_login(vha, fcport); |
| break; |
| |
| case MODE_TARGET: |
| default: |
| /* no-op */ |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static |
| void qla24xx_handle_relogin_event(scsi_qla_host_t *vha, |
| struct event_arg *ea) |
| { |
| fc_port_t *fcport = ea->fcport; |
| |
| if (fcport->scan_state != QLA_FCPORT_FOUND) { |
| fcport->login_retry++; |
| return; |
| } |
| |
| 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)) |
| return; |
| } |
| |
| if (fcport->flags & FCF_ASYNC_SENT) { |
| fcport->login_retry++; |
| set_bit(RELOGIN_NEEDED, &vha->dpc_flags); |
| return; |
| } |
| |
| if (fcport->disc_state == DSC_DELETE_PEND) { |
| fcport->login_retry++; |
| 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_async_gidpn(vha, fcport); |
| return; |
| } |
| |
| qla24xx_fcport_handle_login(vha, fcport); |
| } |
| |
| void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) |
| { |
| fc_port_t *fcport, *f, *tf; |
| uint32_t id = 0, mask, rid; |
| int rc; |
| |
| switch (ea->event) { |
| case FCME_RELOGIN: |
| case FCME_RSCN: |
| case FCME_GIDPN_DONE: |
| case FCME_GPSC_DONE: |
| case FCME_GPNID_DONE: |
| if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) || |
| test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags)) |
| return; |
| break; |
| default: |
| break; |
| } |
| |
| 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) { |
| /* cable moved */ |
| rc = qla24xx_post_gpnid_work(vha, &ea->id); |
| if (rc) { |
| ql_log(ql_log_warn, vha, 0xd044, |
| "RSCN GPNID work failed %02x%02x%02x\n", |
| ea->id.b.domain, ea->id.b.area, |
| ea->id.b.al_pa); |
| } |
| } else { |
| ea->fcport = fcport; |
| qla24xx_handle_rscn_event(fcport, ea); |
| } |
| 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_post_upd_fcport_work(vha, ea->fcport); |
| 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_DELETE_DONE: |
| qla24xx_handle_delete_done_event(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"; |
| 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; |
| tm_iocb->timeout = qla2x00_tmf_iocb_timeout; |
| init_completion(&tm_iocb->u.tmf.comp); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| 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); |
| |
| wait_for_completion(&tm_iocb->u.tmf.comp); |
| |
| rval = tm_iocb->u.tmf.comp_status == CS_COMPLETE ? |
| QLA_SUCCESS : QLA_FUNCTION_FAILED; |
| |
| if ((rval != QLA_SUCCESS) || tm_iocb->u.tmf.data) { |
| ql_dbg(ql_dbg_taskm, 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], sp->fcport->loop_id, lun, |
| flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); |
| } |
| |
| done_free_sp: |
| sp->free(sp); |
| 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; |
| complete(&abt->u.abt.comp); |
| } |
| |
| static void |
| qla24xx_abort_sp_done(void *ptr, int res) |
| { |
| srb_t *sp = ptr; |
| struct srb_iocb *abt = &sp->u.iocb_cmd; |
| |
| complete(&abt->u.abt.comp); |
| } |
| |
| int |
| qla24xx_async_abort_cmd(srb_t *cmd_sp) |
| { |
| scsi_qla_host_t *vha = cmd_sp->vha; |
| fc_port_t *fcport = cmd_sp->fcport; |
| struct srb_iocb *abt_iocb; |
| srb_t *sp; |
| int rval = QLA_FUNCTION_FAILED; |
| |
| sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); |
| if (!sp) |
| goto done; |
| |
| abt_iocb = &sp->u.iocb_cmd; |
| sp->type = SRB_ABT_CMD; |
| sp->name = "abort"; |
| qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)); |
| abt_iocb->u.abt.cmd_hndl = cmd_sp->handle; |
| sp->done = qla24xx_abort_sp_done; |
| abt_iocb->timeout = qla24xx_abort_iocb_timeout; |
| init_completion(&abt_iocb->u.abt.comp); |
| |
| rval = qla2x00_start_sp(sp); |
| if (rval != QLA_SUCCESS) |
| goto done_free_sp; |
| |
| ql_dbg(ql_dbg_async, vha, 0x507c, |
| "Abort command issued - hdl=%x, target_id=%x\n", |
| cmd_sp->handle, fcport->tgt_id); |
| |
| wait_for_completion(&abt_iocb->u.abt.comp); |
| |
| rval = abt_iocb->u.abt.comp_status == CS_COMPLETE ? |
| QLA_SUCCESS : QLA_FUNCTION_FAILED; |
| |
| 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; |
| |
| 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); |
| } |
| |
| 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: |
| 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 */ |
| |
| 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 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; |
| ea->fcport->send_els_logo = 0; |
| 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 LoopID 0x%x in use post gnl\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->loop_id); |
| |
| if (IS_SW_RESV_ADDR(cid)) { |
| set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); |
| ea->fcport->loop_id = FC_NO_LOOP_ID; |
| } else { |
| qla2x00_clear_loop_id(ea->fcport); |
| } |
| qla24xx_post_gnl_work(vha, ea->fcport); |
| break; |
| case MBS_PORT_ID_USED: |
| ql_dbg(ql_dbg_disc, vha, 0x20ed, |
| "%s %d %8phC NPortId %02x%02x%02x inuse post gidpn\n", |
| __func__, __LINE__, ea->fcport->port_name, |
| ea->fcport->d_id.b.domain, ea->fcport->d_id.b.area, |
| ea->fcport->d_id.b.al_pa); |
| |
| qla2x00_clear_loop_id(ea->fcport); |
| qla24xx_post_gidpn_work(vha, ea->fcport); |
| break; |
| } |
| return; |
| } |
| |
| void |
| qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| qla2x00_mark_device_lost(vha, fcport, 1, 0); |
| qlt_logo_completion_handler(fcport, data[0]); |
| fcport->login_gen++; |
| return; |
| } |
| |
| void |
| qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, |
| uint16_t *data) |
| { |
| if (data[0] == MBS_COMMAND_COMPLETE) { |
| qla2x00_update_fcport(vha, fcport); |
| |
| return; |
| } |
| |
| /* Retry login. */ |
| fcport->flags &= ~FCF_ASYNC_SENT; |
| 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. |
| * @ha: 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. |
| * @ha: 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. |
| * @ha: 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. |
| * @ha: 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. |
| * @ha: 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. |
| * @ha: 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. |
| * |
| * 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. |
| * @ha: 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. |
| * @ha: 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. |
| * @ha: 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 %lx.\n", (u_long)®->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) { |
| ql_log(ql_log_warn, vha, 0x0062, |
| "Wrong product ID = 0x%x,0x%x,0x%x.\n", |
| mb[1], mb[2], mb[3]); |
| |
| goto chip_diag_failed; |
| } |
| ha->product_id[0] = mb[1]; |
| ha->product_id[1] = mb[2]; |
| ha->product_id[2] = mb[3]; |
| ha->product_id[3] = mb[4]; |
| |
| /* Adjust fw RISC transfer size */ |
| if (req->length > 1024) |
| ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024; |
| else |
| ha->fw_transfer_size = REQUEST_ENTRY_SIZE * |
| req->length; |
| |
| if (IS_QLA2200(ha) && |
| RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) { |
| /* Limit firmware transfer size with a 2200A */ |
| ql_dbg(ql_dbg_init, vha, 0x007e, "Found QLA2200A Chip.\n"); |
| |
| ha->device_type |= DT_ISP2200A; |
| ha->fw_transfer_size = 128; |
| } |
| |
| /* Wrap Incoming Mailboxes Test. */ |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| ql_dbg(ql_dbg_init, vha, 0x007f, "Checking mailboxes.\n"); |
| rval = qla2x00_mbx_reg_test(vha); |
| if (rval) |
| ql_log(ql_log_warn, vha, 0x0080, |
| "Failed mailbox send register test.\n"); |
| else |
| /* Flag a successful rval */ |
| rval = QLA_SUCCESS; |
| spin_lock_irqsave(&ha->hardware_lock, flags); |
| |
| chip_diag_failed: |
| if (rval) |
| ql_log(ql_log_info, vha, 0x0081, |
| "Chip diagnostics **** FAILED ****.\n"); |
| |
| spin_unlock_irqrestore(&ha->hardware_lock, flags); |
| |
| return (rval); |
| } |
| |
| /** |
| * qla24xx_chip_diag() - Test ISP24xx for proper operation. |
| * @ha: HA context |
| * |
| * Returns 0 on success. |
| */ |
| int |
| qla24xx_chip_diag(scsi_qla_host_t *vha) |
| { |
| int rval; |
| struct qla_hw_data *ha = vha->hw; |
| struct req_que *req = ha->req_q_map[0]; |
| |
| if (IS_P3P_TYPE(ha)) |
| return QLA_SUCCESS; |
| |
| ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length; |
| |
| rval = qla2x00_mbx_reg_test(vha); |
| if (rval) { |
| ql_log(ql_log_warn, vha, 0x0082, |
| "Failed mailbox send register test.\n"); |
| } else { |
| /* Flag a successful rval */ |
| rval = QLA_SUCCESS; |
| } |
| |
| return rval; |
| } |
| |
| void |
| qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) |
| { |
| int rval; |
| uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size, |
| eft_size, fce_size, mq_size; |
| dma_addr_t tc_dma; |
| void *tc; |
| struct qla_hw_data *ha = vha->hw; |
| struct req_que *req = ha->req_q_map[0]; |
| struct rsp_que *rsp = ha->rsp_q_map[0]; |
| |
| if (ha->fw_dump) { |
| ql_dbg(ql_dbg_init, vha, 0x00bd, |
| "Firmware dump already allocated.\n"); |
| return; |
| } |
| |
| ha->fw_dumped = 0; |
| ha->fw_dump_cap_flags = 0; |
| dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0; |
| req_q_size = rsp_q_size = 0; |
| |
| if (IS_QLA27XX(ha)) |
| goto try_fce; |
| |
| if (IS_QLA2100(ha) || IS_QLA2200(ha)) { |
| fixed_size = sizeof(struct qla2100_fw_dump); |
| } else if (IS_QLA23XX(ha)) { |
| fixed_size = offsetof(struct qla2300_fw_dump, data_ram); |
| mem_size = (ha->fw_memory_size - 0x11000 + 1) * |
| sizeof(uint16_t); |
| } else if (IS_FWI2_CAPABLE(ha)) { |
| if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) |
| fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem); |
| else if (IS_QLA81XX(ha)) |
| fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem); |
| else if (IS_QLA25XX(ha)) |
| fixed_size = offsetof(struct qla25xx_fw_dump, ext_mem); |
| else |
| fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem); |
| |
| mem_size = (ha->fw_memory_size - 0x100000 + 1) * |
| sizeof(uint32_t); |
| if (ha->mqenable) { |
| if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) |
| mq_size = sizeof(struct qla2xxx_mq_chain); |
| /* |
| * Allocate maximum buffer size for all queues. |
| * Resizing must be done at end-of-dump processing. |
| */ |
| mq_size += ha->max_req_queues * |
| (req->length * sizeof(request_t)); |
| mq_size += ha->max_rsp_queues * |
| (rsp->length * sizeof(response_t)); |
| } |
| if (ha->tgt.atio_ring) |
| mq_size += ha->tgt.atio_q_length * sizeof(request_t); |
| /* Allocate memory for Fibre Channel Event Buffer. */ |
| if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && |
| !IS_QLA27XX(ha)) |
| goto try_eft; |
| |
| try_fce: |
| if (ha->fce) |
| dma_free_coherent(&ha->pdev->dev, |
| FCE_SIZE, ha->fce, ha->fce_dma); |
| |
| /* Allocate memory for Fibre Channel Event Buffer. */ |
| tc = dma_zalloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, |
| GFP_KERNEL); |
| if (!tc) { |
| ql_log(ql_log_warn, vha, 0x00be, |
| "Unable to allocate (%d KB) for FCE.\n", |
| FCE_SIZE / 1024); |
| goto try_eft; |
| } |
| |
| rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS, |
| ha->fce_mb, &ha->fce_bufs); |
| if (rval) { |
| ql_log(ql_log_warn, vha, 0x00bf, |
| "Unable to initialize FCE (%d).\n", rval); |
| dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc, |
| tc_dma); |
| ha->flags.fce_enabled = 0; |
| goto try_eft; |
| } |
| ql_dbg(ql_dbg_init, vha, 0x00c0, |
| "Allocate (%d KB) for FCE...\n", FCE_SIZE / 1024); |
| |
| fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE; |
| ha->flags.fce_enabled = 1; |
| ha->fce_dma = tc_dma; |
| ha->fce = tc; |
| |
| try_eft: |
| if (ha->eft) |
| dma_free_coherent(&ha->pdev->dev, |
| EFT_SIZE, ha->eft, ha->eft_dma); |
| |
| /* Allocate memory for Extended Trace Buffer. */ |
| tc = dma_zalloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, |
| GFP_KERNEL); |
| if (!tc) { |
| ql_log(ql_log_warn, vha, 0x00c1, |
| "Unable to allocate (%d KB) for EFT.\n", |
| EFT_SIZE / 1024); |
| goto cont_alloc; |
| } |
| |
| rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS); |
| if (rval) { |
| ql_log(ql_log_warn, vha, 0x00c2, |
| "Unable to initialize EFT (%d).\n", rval); |
| dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc, |
| tc_dma); |
| goto cont_alloc; |
| } |
| ql_dbg(ql_dbg_init, vha, 0x00c3, |
| "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024); |
| |
| eft_size = EFT_SIZE; |
| ha->eft_dma = tc_dma; |
| ha->eft = tc; |
| } |
| |
| cont_alloc: |
| if (IS_QLA27XX(ha)) { |
| if (!ha->fw_dump_template) { |
| ql_log(ql_log_warn, vha, 0x00ba, |
| "Failed missing fwdump template\n"); |
| return; |
| } |
| dump_size = qla27xx_fwdt_calculate_dump_size(vha); |
| ql_dbg(ql_dbg_init, vha, 0x00fa, |
| "-> allocating fwdump (%x bytes)...\n", dump_size); |
| goto allocate; |
| } |
| |
| req_q_size = req->length * sizeof(request_t); |
| rsp_q_size = rsp->length * sizeof(response_t); |
| dump_size = offsetof(struct qla2xxx_fw_dump, isp); |
| dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size; |
| ha->chain_offset = dump_size; |
| dump_size += mq_size + fce_size; |
| |
| if (ha->exchoffld_buf) |
| dump_size += sizeof(struct qla2xxx_offld_chain) + |
| ha->exchoffld_size; |
| if (ha->exlogin_buf) |
| dump_size += sizeof(struct qla2xxx_offld_chain) + |
| ha->exlogin_size; |
| |
| allocate: |
| ha->fw_dump = vmalloc(dump_size); |
| if (!ha->fw_dump) { |
| ql_log(ql_log_warn, vha, 0x00c4, |
| "Unable to allocate (%d KB) for firmware dump.\n", |
| dump_size / 1024); |
| |
| if (ha->fce) { |
| dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, |
| ha->fce_dma); |
| ha->fce = NULL; |
| ha->fce_dma = 0; |
| } |
| |
| if (ha->eft) { |
| dma_free_coherent(&ha->pdev->dev, eft_size, ha->eft, |
| ha->eft_dma); |
| ha->eft = NULL; |
| ha->eft_dma = 0; |
| } |
| return; |
| } |
| ha->fw_dump_len = dump_size; |
| ql_dbg(ql_dbg_init, vha, 0x00c5, |
| "Allocated (%d KB) for firmware dump.\n", dump_size / 1024); |
| |
| if (IS_QLA27XX(ha)) |
| return; |
| |
| ha->fw_dump->signature[0] = 'Q'; |
| ha->fw_dump->signature[1] = 'L'; |
| ha->fw_dump->signature[2] = 'G'; |
| ha->fw_dump->signature[3] = 'C'; |
| ha->fw_dump->version = htonl(1); |
| |
| ha->fw_dump->fixed_size = htonl(fixed_size); |
| ha->fw_dump->mem_size = htonl(mem_size); |
| ha->fw_dump->req_q_size = htonl(req_q_size); |
| ha->fw_dump->rsp_q_size = htonl(rsp_q_size); |
| |
| ha->fw_dump->eft_size = htonl(eft_size); |
| ha->fw_dump->eft_addr_l = htonl(LSD(ha->eft_dma)); |
| ha->fw_dump->eft_addr_h = htonl(MSD(ha->eft_dma)); |
| |
| ha->fw_dump->header_size = |
| htonl(offsetof(struct qla2xxx_fw_dump, isp)); |
| } |
| |
| static int |
| qla81xx_mpi_sync(scsi_qla_host_t *vha) |
| { |
| #define MPS_MASK 0xe0 |
| int rval; |
| uint16_t dc; |
| uint32_t dw; |
| |
| if (!IS_QLA81XX(vha->hw)) |
| return QLA_SUCCESS; |
| |
| rval = qla2x00_write_ram_word(vha, 0x7c00, 1); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x0105, |
| "Unable to acquire semaphore.\n"); |
| goto done; |
| } |
| |
| pci_read_config_word(vha->hw->pdev, 0x54, &dc); |
| rval = qla2x00_read_ram_word(vha, 0x7a15, &dw); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x0067, "Unable to read sync.\n"); |
| goto done_release; |
| } |
| |
| dc &= MPS_MASK; |
| if (dc == (dw & MPS_MASK)) |
| goto done_release; |
| |
| dw &= ~MPS_MASK; |
| dw |= dc; |
| rval = qla2x00_write_ram_word(vha, 0x7a15, dw); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x0114, "Unable to gain sync.\n"); |
| } |
| |
| done_release: |
| rval = qla2x00_write_ram_word(vha, 0x7c00, 0); |
| if (rval != QLA_SUCCESS) { |
| ql_log(ql_log_warn, vha, 0x006d, |
| "Unable to release semaphore.\n"); |
| } |
| |
| done: |
| return rval; |
| } |
| |
| int |
| qla2x00_alloc_outstanding_cmds(struct qla_hw_data *ha, struct req_que *req) |
| { |
| /* Don't try to reallocate the array */ |
| if (req->outstanding_cmds) |
| return QLA_SUCCESS; |
| |
| if (!IS_FWI2_CAPABLE(ha)) |
| req->num_outstanding_cmds = DEFAULT_OUTSTANDING_COMMANDS; |
| else { |
| if (ha->cur_fw_xcb_count <= ha->cur_fw_iocb_count) |
| req->num_outstanding_cmds = ha->cur_fw_xcb_count; |
| else |
| req->num_outstanding_cmds = ha->cur_fw_iocb_count; |
| } |
| |
| req->outstanding_cmds = kzalloc(sizeof(srb_t *) * |
| req->num_outstanding_cmds, GFP_KERNEL); |
| |
| if (!req->outstanding_cmds) { |
| /* |
| * Try to allocate a minimal size just so we can get through |
| * initialization. |
| */ |
| req->num_outstanding_cmds = MIN_OUTSTANDING_COMMANDS; |
| req->outstanding_cmds = kzalloc(sizeof(srb_t *) * |
| req->num_outstanding_cmds, GFP_KERNEL); |
| |
| if (!req->outstanding_cmds) { |
| ql_log(ql_log_fatal, NULL, 0x0126, |
| "Failed to allocate memory for " |
| "outstanding_cmds for req_que %p.\n", req); |
| req->num_outstanding_cmds = 0; |
| return QLA_FUNCTION_FAILED; |
| } |
| } |
| |
| return QLA_SUCCESS; |
| } |
| |
| #define PRINT_FIELD(_field, _flag, _str) { \ |
| if (a0->_field & _flag) {\ |
| if (p) {\ |
| strcat(ptr, "|");\ |
| ptr++;\ |
| leftover--;\ |
| } \ |
| len = snprintf(ptr, leftover, "%s", _str); \ |
| p = 1;\ |
| leftover -= len;\ |
| ptr += len; \ |
| } \ |
| } |
| |
| static void qla2xxx_print_sfp_info(struct scsi_qla_host *vha) |
| { |
| #define STR_LEN 64 |
| struct sff_8247_a0 *a0 = (struct sff_8247_a0 *)vha->hw->sfp_data; |
| u8 str[STR_LEN], *ptr, p; |
| int leftover, len; |
| |
| memset(str, 0, STR_LEN); |
| snprintf(str, SFF_VEN_NAME_LEN+1, a0->vendor_name); |
| ql_dbg(ql_dbg_init, vha, 0x015a, |
| "SFP MFG Name: %s\n", str); |
| |
| memset(str, 0, STR_LEN); |
| snprintf(str, SFF_PART_NAME_LEN+1, a0->vendor_pn); |
| ql_dbg(ql_dbg_init, vha, 0x015c, |
| "SFP Part Name: %s\n", str); |
| |
| /* media */ |
| memset(str, 0, STR_LEN); |
| ptr = str; |
| leftover = STR_LEN; |
| p = len = 0; |
| PRINT_FIELD(fc_med_cc9, FC_MED_TW, "Twin AX"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_TP, "Twisted Pair"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_MI, "Min Coax"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_TV, "Video Coax"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_M6, "MultiMode 62.5um"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_M5, "MultiMode 50um"); |
| PRINT_FIELD(fc_med_cc9, FC_MED_SM, "SingleMode"); |
| ql_dbg(ql_dbg_init, vha, 0x0160, |
| "SFP Media: %s\n", str); |
| |
| /* link length */ |
| memset(str, 0, STR_LEN); |
| ptr = str; |
| leftover = STR_LEN; |
| p = len = 0; |
| PRINT_FIELD(fc_ll_cc7, FC_LL_VL, "Very Long"); |
| PRINT_FIELD(fc_ll_cc7, FC_LL_S, "Short"); |
| PRINT_FIELD(fc_ll_cc7, FC_LL_I, "Intermediate"); |
| PRINT_FIELD(fc_ll_cc7, FC_LL_L, "Long"); |
| PRINT_FIELD(fc_ll_cc7, FC_LL_M, "Medium"); |
| ql_dbg(ql_dbg_init, vha, 0x0196, |
| "SFP Link Length: %s\n", str); |
| |
| memset(str, 0, STR_LEN); |
| ptr = str; |
| leftover = STR_LEN; |
| p = len = 0; |
| PRINT_FIELD(fc_ll_cc7, FC_LL_SA, "Short Wave (SA)"); |
| PRINT_FIELD(fc_ll_cc7, FC_LL_LC, "Long Wave(LC)"); |
| PRINT_FIELD(fc_tec_cc8, FC_TEC_SN, "Short Wave (SN)"); |
| PRINT_FIELD(fc_tec_cc8, FC_TEC_SL, "Short Wave (SL)"); |
| PRINT_FIELD(fc_tec_cc8, FC_TEC_LL, "Long Wave (LL)"); |
| ql_dbg(ql_dbg_init, vha, 0x016e, |
| "SFP FC Link Tech: %s\n", str); |
| |
| if (a0->length_km) |
| ql_dbg(ql_dbg_init, vha, 0x016f, |
| "SFP Distant: %d km\n", a0->length_km); |
| if (a0->length_100m) |
| ql_dbg(ql_dbg_init, vha, 0x0170, |
| "SFP Distant: %d m\n", a0->length_100m*100); |
| if (a0->length_50um_10m) |
| ql_dbg(ql_dbg_init, vha, 0x0189, |
| "SFP Distant (WL=50um): %d m\n", a0->length_50um_10m * 10); |
| if (a0->length_62um_10m) |
| ql_dbg(ql_dbg_init, vha, 0x018a, |
| "SFP Distant (WL=62.5um): %d m\n", a0->length_62um_10m * 10); |
| if (a0->length_om4_10m) |
| ql_dbg(ql_dbg_init, vha, 0x0194, |
| "SFP Distant (OM4): %d m\n", a0->length_om4_10m * 10); |
| if (a0->length_om3_10m) |
| ql_dbg(ql_dbg_init, vha, 0x0195, |
| "SFP Distant (OM3): %d m\n", a0->length_om3_10m * 10); |
| } |
| |
| |
| /* |
| * Return Code: |
| * QLA_SUCCESS: no action |
| * QLA_INTERFACE_ERROR: SFP is not there. |
| * QLA_FUNCTION_FAILED: detected New SFP |
| */ |
| int |
| qla24xx_detect_sfp(scsi_qla_host_t *vha) |
| { |
| int rc = QLA_SUCCESS; |
| struct sff_8247_a0 *a; |
| struct qla_hw_data *ha = vha->hw; |
| |
| if (!AUTO_DETECT_SFP_SUPPORT(vha)) |
| goto out; |
| |
| rc = qla2x00_read_sfp_dev(vha, NULL, 0); |
| if (rc) |
| goto out; |
| |
| a = (struct sff_8247_a0 *)vha->hw->sfp_data; |
| qla2xxx_print_sfp_info(vha); |
| |
| if (a->fc_ll_cc7 & FC_LL_VL || a->fc_ll_cc7 & FC_LL_L) { |
| /* long range */ |
| ha->flags.detected_lr_sfp = 1; |
| |
| if (a->length_km > 5 || a->length_100m > 50) |
| ha->long_range_distance = LR_DISTANCE_10K; |
| else |
| ha->long_range_distance = LR_DISTANCE_5K; |
| |
| if (ha->flags.detected_lr_sfp != ha->flags.using_lr_setting) |
| ql_dbg(ql_dbg_async, vha, 0x507b, |
| "Detected Long Range SFP.\n"); |
| } else { |
| /* short range */ |
| ha->flags.detected_lr_sfp = 0; |
| if (ha->flags.using_lr_setting) |
| ql_dbg(ql_dbg_async, vha, 0x5084, |
| "Detected Short Range SFP.\n"); |
| } |
| |
| if (!vha->flags.init_done) |
| rc = QLA_SUCCESS; |
| out: |
| return rc; |
| } |
| |
| /** |
| * qla2x00_setup_chip() - L
|