blob: b5b48ddca9621d82bb9b7b674b8eb9f79f083fbe [file] [log] [blame]
/*
* 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(&reg->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(&reg->hccr, HCCR_PAUSE_RISC);
for (cnt = 0; cnt < 30000; cnt++) {
if ((RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) != 0)
break;
udelay(10);
}
/* Select FPM registers. */
WRT_REG_WORD(&reg->ctrl_status, 0x20);
RD_REG_WORD(&reg->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(&reg->ctrl_status, 0x0);
RD_REG_WORD(&reg->ctrl_status);
/* Release RISC module. */
WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
for (cnt = 0; cnt < 30000; cnt++) {
if ((RD_REG_WORD(&reg->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(&reg->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(&reg->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(&reg->hccr, HCCR_PAUSE_RISC);
if (IS_QLA2200(ha) || IS_QLA2300(ha)) {
for (cnt = 0; cnt < 30000; cnt++) {
if ((RD_REG_WORD(&reg->hccr) &
HCCR_RISC_PAUSE) != 0)
break;
udelay(100);
}
} else {
RD_REG_WORD(&reg->hccr); /* PCI Posting. */
udelay(10);
}
/* Select FPM registers. */
WRT_REG_WORD(&reg->ctrl_status, 0x20);
RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
/* FPM Soft Reset. */
WRT_REG_WORD(&reg->fpm_diag_config, 0x100);
RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
/* Toggle Fpm Reset. */
if (!IS_QLA2200(ha)) {
WRT_REG_WORD(&reg->fpm_diag_config, 0x0);
RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
}
/* Select frame buffer registers. */
WRT_REG_WORD(&reg->ctrl_status, 0x10);
RD_REG_WORD(&reg->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(&reg->ctrl_status, 0);
RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
/* Reset RISC processor. */
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr); /* PCI Posting. */
/* Release RISC processor. */
WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
RD_REG_WORD(&reg->hccr); /* PCI Posting. */
}
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
WRT_REG_WORD(&reg->hccr, HCCR_CLR_HOST_INT);
/* Reset ISP chip. */
WRT_REG_WORD(&reg->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(&reg->ctrl_status) &
CSR_ISP_SOFT_RESET) == 0)
break;
udelay(100);
}
} else
udelay(10);
/* Reset RISC processor. */
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
WRT_REG_WORD(&reg->semaphore, 0);
/* Release RISC processor. */
WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
RD_REG_WORD(&reg->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(&reg->hccr, HCCR_DISABLE_PARITY_PAUSE);
RD_REG_WORD(&reg->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(&reg->ctrl_status, CSRX_DMA_SHUTDOWN|MWB_4096_BYTES);
for (cnt = 0; cnt < 30000; cnt++) {
if ((RD_REG_DWORD(&reg->ctrl_status) & CSRX_DMA_ACTIVE) == 0)
break;
udelay(10);
}
if (!(RD_REG_DWORD(&reg->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(&reg->hccr),
RD_REG_DWORD(&reg->ctrl_status),
(RD_REG_DWORD(&reg->ctrl_status) & CSRX_DMA_ACTIVE));
WRT_REG_DWORD(&reg->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(&reg->mailbox0);
for (cnt = 10000; RD_REG_WORD(&reg->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(&reg->hccr),
RD_REG_DWORD(&reg->mailbox0));
/* Wait for soft-reset to complete. */
RD_REG_DWORD(&reg->ctrl_status);
for (cnt = 0; cnt < 60; cnt++) {
barrier();
if ((RD_REG_DWORD(&reg->ctrl_status) &
CSRX_ISP_SOFT_RESET) == 0)
break;
udelay(5);
}
if (!(RD_REG_DWORD(&reg->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(&reg->hccr),
RD_REG_DWORD(&reg->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(&reg->hccr, HCCRX_SET_RISC_RESET);
RD_REG_DWORD(&reg->hccr);
WRT_REG_DWORD(&reg->hccr, HCCRX_REL_RISC_PAUSE);
RD_REG_DWORD(&reg->hccr);
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_RESET);
RD_REG_DWORD(&reg->hccr);
RD_REG_WORD(&reg->mailbox0);
for (cnt = 60; RD_REG_WORD(&reg->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(&reg->hccr),
RD_REG_WORD(&reg->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(&reg->iobase_addr, RISC_REGISTER_BASE_OFFSET);
*data = RD_REG_DWORD(&reg->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(&reg->iobase_addr, RISC_REGISTER_BASE_OFFSET);
WRT_REG_DWORD(&reg->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)&reg->flash_address);
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Reset ISP chip. */
WRT_REG_WORD(&reg->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(&reg->ctrl_status);
for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) {
udelay(5);
data = RD_REG_WORD(&reg->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(&reg->hccr, HCCR_RESET_RISC);
WRT_REG_WORD(&reg->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