668 lines
22 KiB
C
668 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Huawei Fabric Channel Linux driver
|
|
* Copyright(c) 2018 Huawei Technologies Co., Ltd
|
|
*
|
|
*/
|
|
|
|
#include "hifc_module.h"
|
|
#include "hifc_service.h"
|
|
|
|
void hifc_build_common_wqe_ctrls(struct hifcoe_wqe_ctrl_s *v_ctrl_sl,
|
|
unsigned char v_task_len)
|
|
{
|
|
/* "BDSL" field of CtrlS - defines the size of BDS,
|
|
* which varies from 0 to 2040 bytes (8 bits of 8 bytes' chunk)
|
|
*/
|
|
v_ctrl_sl->ch.wd0.bdsl = 0;
|
|
|
|
/*
|
|
* "DrvSL" field of CtrlS - defines the size of DrvS, which varies from
|
|
* 0 to 24 bytes
|
|
*/
|
|
v_ctrl_sl->ch.wd0.drv_sl = 0;
|
|
|
|
/* a.
|
|
* b1 - linking WQE, which will be only used in linked page architecture
|
|
* instead of ring, it's a special control WQE which does not contain
|
|
* any buffer or inline data information, and will only be consumed by
|
|
* hardware. The size is aligned to WQEBB/WQE b0 - normal WQE, either
|
|
* normal SEG WQE or inline data WQE
|
|
*/
|
|
v_ctrl_sl->ch.wd0.wf = 0;
|
|
|
|
/*
|
|
* "CF" field of CtrlS - Completion Format - defines the format of CS.
|
|
* a.b0 - Status information is embedded inside of Completion Section
|
|
* b.b1 - Completion Section keeps SGL, where Status information
|
|
* should be written. (For the definition of SGLs see ?4.1* .)
|
|
*/
|
|
v_ctrl_sl->ch.wd0.cf = 0;
|
|
|
|
/*
|
|
* "TSL" field of CtrlS - defines the size of TS, which varies from 0
|
|
* to 248 bytes
|
|
*/
|
|
v_ctrl_sl->ch.wd0.tsl = v_task_len;
|
|
|
|
/*
|
|
* Variable length SGE (vSGE). The size of SGE is 16 bytes. The vSGE
|
|
* format is of two types, which are defined by "VA " field of CtrlS.
|
|
* "VA" stands for Virtual Address: o b0. SGE comprises 64-bits buffer's
|
|
* pointer and 31-bits Length, each SGE can only support up to 2G-1B,
|
|
* it can guarantee each single SGE length can not exceed 2GB by nature,
|
|
* A byte count value of zero means a 0byte data transfer.o b1.
|
|
* SGE comprises 64-bits buffer's pointer, 31-bits Length and 30-bits
|
|
* Key of the Translation table ,each SGE can only support up to 2G-1B,
|
|
* it can guarantee each single SGE length can notexceed 2GB by nature,
|
|
* A byte count value of zero means a 0byte data transfer
|
|
*/
|
|
v_ctrl_sl->ch.wd0.va = 0;
|
|
|
|
/*
|
|
* "DF" field of CtrlS - Data Format - defines the format of BDS
|
|
* a. b0 - BDS carries the list of SGEs (SGL)
|
|
* b. b1 - BDS carries the inline data
|
|
*/
|
|
v_ctrl_sl->ch.wd0.df = 0;
|
|
|
|
/*
|
|
* "CR" - Completion is Required - marks CQE generation request per WQE
|
|
*/
|
|
v_ctrl_sl->ch.wd0.cr = 1;
|
|
|
|
/*
|
|
* "DIFSL" field of CtrlS - defines the size of DIFS, which varies from
|
|
* 0 to 56 bytes
|
|
*/
|
|
v_ctrl_sl->ch.wd0.dif_sl = 0;
|
|
|
|
/*
|
|
* "CSL" field of CtrlS - defines the size of CS, which varies from 0 to
|
|
* 24 bytes
|
|
*/
|
|
v_ctrl_sl->ch.wd0.csl = 0;
|
|
|
|
/* "CtrlSL" - C describes the size of CtrlS in 8 bytes chunks.
|
|
*The value Zero is not valid
|
|
*/
|
|
v_ctrl_sl->ch.wd0.ctrl_sl = 1;
|
|
|
|
/* "O" - Owner - marks ownership of WQE */
|
|
v_ctrl_sl->ch.wd0.owner = 0;
|
|
}
|
|
|
|
void hifc_build_trd_twr_wqe_ctrls(struct unf_frame_pkg_s *v_pkg,
|
|
struct hifcoe_sqe_s *v_sqe)
|
|
{
|
|
/* "BDSL" field of CtrlS - defines the size of BDS, which varies from
|
|
* 0 to 2040 bytes (8 bits of 8 bytes' chunk)
|
|
*/
|
|
/* TrdWqe carry 2 SGE defaultly, 4DW per SGE, the value is 4 because
|
|
* unit is 2DW, in double SGL mode, bdsl is 2
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.bdsl = HIFC_T_RD_WR_WQE_CTR_BDSL_SIZE;
|
|
|
|
/*
|
|
* "DrvSL" field of CtrlS - defines the size of DrvS, which varies from
|
|
* 0 to 24 bytes DrvSL config for 0
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.drv_sl = 0;
|
|
|
|
/* a. b1 - linking WQE, which will be only used in linked page
|
|
* architecture instead of ring, it's a special control WQE which does
|
|
* not contain any buffer or inline data information, and will only be
|
|
* consumed by hardware. The size is aligned to WQEBB/WQE b0 - normal
|
|
* WQE, either normal SEG WQE or inline data WQE
|
|
*/
|
|
/* normal wqe */
|
|
v_sqe->ctrl_sl.ch.wd0.wf = 0;
|
|
|
|
/*
|
|
* "CF" field of CtrlS - Completion Format - defines the format of CS.
|
|
* a.b0 - Status information is embedded inside of Completion Section
|
|
* b.b1 - Completion Section keeps SGL, where Status information
|
|
* should be written. (For the definition of SGLs see ?4.1.)
|
|
*/
|
|
/* by SCQE mode, the value is ignored */
|
|
v_sqe->ctrl_sl.ch.wd0.cf = 0;
|
|
|
|
/* "TSL" field of CtrlS - defines the size of TS, which varies from 0 to
|
|
* 248 bytes
|
|
*/
|
|
/* TSL is configured by 56 bytes */
|
|
v_sqe->ctrl_sl.ch.wd0.tsl = sizeof(struct hifcoe_sqe_ts_s) /
|
|
HIFC_WQE_SECTION_CHUNK_SIZE;
|
|
|
|
/*
|
|
* Variable length SGE (vSGE). The size of SGE is 16 bytes. The vSGE
|
|
* format is of two types, which are defined by "VA" field of CtrlS.
|
|
* "VA" stands for Virtual Address: o b0. SGE comprises 64-bits buffer's
|
|
* pointer and 31-bits Length, each SGE can only support up to 2G-1B, it
|
|
* can guarantee each single SGE length can not exceed 2GB by nature, A
|
|
* byte count value of zero means a 0byte data transfer. o b1. SGE
|
|
* comprises 64-bits buffer's pointer, 31-bits Length and 30-bits Key of
|
|
* the Translation table , each SGE can only support up to 2G-1B, it can
|
|
* guarantee each single SGE length can not exceed 2GB by nature, A byte
|
|
* count value of zero means a 0byte data transfer
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.va = 0;
|
|
|
|
/*
|
|
* "DF" field of CtrlS - Data Format - defines the format of BDS
|
|
* a. b0 - BDS carries the list of SGEs (SGL)
|
|
* b. b1 - BDS carries the inline data
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.df = 0;
|
|
|
|
/* "CR" - Completion is Required marks CQE generation request per WQE */
|
|
/* by SCQE mode, this value is ignored */
|
|
v_sqe->ctrl_sl.ch.wd0.cr = 1;
|
|
|
|
/*
|
|
* "DIFSL" field of CtrlS - defines the size of DIFS, which varies from
|
|
* 0 to 56 bytes.
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.dif_sl = 0;
|
|
|
|
/*
|
|
* "CSL" field of CtrlS - defines the size of CS, which varies from 0 to
|
|
* 24 bytes
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.csl = 0;
|
|
|
|
/* "CtrlSL" - C describes the size of CtrlS in 8 bytes chunks.
|
|
* The value Zero is not valid.
|
|
*/
|
|
v_sqe->ctrl_sl.ch.wd0.ctrl_sl = HIFC_T_RD_WR_WQE_CTR_CTRLSL_SIZE;
|
|
|
|
/* "O" - Owner - marks ownership of WQE */
|
|
v_sqe->ctrl_sl.ch.wd0.owner = 0;
|
|
}
|
|
|
|
void hifc_build_service_wqe_ts_common(struct hifcoe_sqe_ts_s *v_sqe_ts,
|
|
unsigned int rport_index,
|
|
unsigned short local_xid,
|
|
unsigned short remote_xid,
|
|
unsigned short data_len)
|
|
{
|
|
v_sqe_ts->local_xid = local_xid;
|
|
|
|
v_sqe_ts->wd0.conn_id = (unsigned short)rport_index;
|
|
v_sqe_ts->wd0.remote_xid = remote_xid;
|
|
|
|
v_sqe_ts->cont.els_gs_elsrsp_comm.data_len = data_len;
|
|
}
|
|
|
|
void hifc_build_els_gs_wqe_sge(struct hifcoe_sqe_s *v_sqe, void *v_buf_addr,
|
|
unsigned long long v_phy_addr,
|
|
unsigned int buf_len,
|
|
unsigned int xid, void *v_hba)
|
|
{
|
|
unsigned long long els_rsp_phy_addr;
|
|
struct hifcoe_variable_sge_s *psge = NULL;
|
|
|
|
/* Fill in SGE and convert it to big-endian. */
|
|
psge = &v_sqe->sge[0];
|
|
els_rsp_phy_addr = v_phy_addr;
|
|
psge->buf_addr_hi = HIFC_HIGH_32_BITS(els_rsp_phy_addr);
|
|
psge->buf_addr_lo = HIFC_LOW_32_BITS(els_rsp_phy_addr);
|
|
psge->wd0.buf_len = buf_len;
|
|
psge->wd0.r_flag = 0;
|
|
psge->wd1.extension_flag = HIFC_WQE_SGE_NOT_EXTEND_FLAG;
|
|
psge->wd1.buf_addr_gpa = (psge->buf_addr_lo >> 16);
|
|
psge->wd1.xid = (xid & 0x3fff);
|
|
psge->wd1.last_flag = HIFC_WQE_SGE_LAST_FLAG;
|
|
hifc_cpu_to_big32(psge, sizeof(*psge));
|
|
|
|
/* Converts the payload of an FC frame into a big end. */
|
|
hifc_cpu_to_big32(v_buf_addr, buf_len);
|
|
}
|
|
|
|
void hifc_build_els_wqe_ts_rsp(struct hifcoe_sqe_s *v_sqe, void *v_sq_info,
|
|
void *v_frame_pld, unsigned short type,
|
|
unsigned short cmnd, unsigned int v_scqn)
|
|
{
|
|
struct unf_pril_payload_s *pri_acc_pld = NULL;
|
|
struct hifcoe_sqe_els_rsp_s *els_rsp = NULL;
|
|
struct hifcoe_sqe_ts_s *sqe_ts = NULL;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
|
|
UNF_CHECK_VALID(0x5015, UNF_TRUE, v_sqe, return);
|
|
UNF_CHECK_VALID(0x5015, UNF_TRUE, v_frame_pld, return);
|
|
UNF_CHECK_VALID(0x5015, UNF_TRUE, v_sq_info, return);
|
|
|
|
sqe_ts = &v_sqe->ts_sl;
|
|
els_rsp = &sqe_ts->cont.els_rsp;
|
|
sqe_ts->task_type = HIFC_SQE_ELS_RSP;
|
|
|
|
/* The default chip does not need to update parameters. */
|
|
els_rsp->wd1.para_update = 0x0;
|
|
|
|
sq_info = (struct hifc_parent_sq_info_s *)v_sq_info;
|
|
hba = (struct hifc_hba_s *)sq_info->phba;
|
|
/* When the PLOGI request is sent, the microcode needs to be instructed
|
|
* to clear the I/O related to the link to avoid data inconsistency
|
|
* caused by the disorder of the IO.
|
|
*/
|
|
if (((cmnd == ELS_LOGO) || (cmnd == ELS_PLOGI)) && hba) {
|
|
els_rsp->wd1.clr_io = 1;
|
|
els_rsp->wd6.reset_exch_start = hba->exit_base;
|
|
els_rsp->wd6.reset_exch_end = hba->exit_base +
|
|
(hba->exit_count - 1);
|
|
els_rsp->wd7.scqn = v_scqn;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"Port(0x%x) send cmd(0x%x) to RPort(0x%x),rport index(0x%x), notify clean io start 0x%x, end 0x%x, scqn 0x%x.",
|
|
sq_info->local_port_id,
|
|
cmnd,
|
|
sq_info->remote_port_id,
|
|
sq_info->rport_index,
|
|
els_rsp->wd6.reset_exch_start,
|
|
els_rsp->wd6.reset_exch_end,
|
|
v_scqn);
|
|
|
|
return;
|
|
}
|
|
|
|
if (type == ELS_RJT)
|
|
return;
|
|
|
|
/*
|
|
* Enter WQE in the PrliAcc negotiation parameter, and fill in the
|
|
* Update flag in WQE.
|
|
*/
|
|
if (cmnd == ELS_PRLI) {
|
|
/* The chip updates the PLOGI ACC negotiation parameters. */
|
|
els_rsp->wd2.seq_cnt = sq_info->plogi_coparams.seq_cnt;
|
|
els_rsp->wd2.e_d_tov = sq_info->plogi_coparams.ed_tov;
|
|
els_rsp->wd2.tx_mfs = sq_info->plogi_coparams.tx_mfs;
|
|
els_rsp->e_d_tov_timer_val =
|
|
sq_info->plogi_coparams.ed_tov_timer_val;
|
|
|
|
/* The chip updates the PRLI ACC parameter. */
|
|
pri_acc_pld = (struct unf_pril_payload_s *)v_frame_pld;
|
|
els_rsp->wd4.xfer_dis = HIFC_GET_PRLI_PARAM_WXFER(
|
|
pri_acc_pld->parms);
|
|
els_rsp->wd4.conf = HIFC_GET_PRLI_PARAM_CONF(
|
|
pri_acc_pld->parms);
|
|
els_rsp->wd4.rec = HIFC_GET_PRLI_PARAM_REC(pri_acc_pld->parms);
|
|
|
|
els_rsp->wd1.para_update = 0x03;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"Port(0x%x) save rport index(0x%x) login parms,seqcnt:0x%x,e_d_tov:0x%x,txmfs:0x%x,e_d_tovtimerval:0x%x,xfer_dis:0x%x, conf:0x%x,rec:0x%x.",
|
|
sq_info->local_port_id,
|
|
sq_info->rport_index, els_rsp->wd2.seq_cnt,
|
|
els_rsp->wd2.e_d_tov, els_rsp->wd2.tx_mfs,
|
|
els_rsp->e_d_tov_timer_val, els_rsp->wd4.xfer_dis,
|
|
els_rsp->wd4.conf, els_rsp->wd4.rec);
|
|
}
|
|
}
|
|
|
|
void hifc_build_els_wqe_ts_req(struct hifcoe_sqe_s *v_sqe, void *v_sq_info,
|
|
unsigned short cmnd, unsigned int v_scqn,
|
|
void *v_frame_pld)
|
|
{
|
|
struct hifcoe_sqe_ts_s *v_sqe_ts = NULL;
|
|
struct hifcoe_sqe_t_els_gs_s *els_req = NULL;
|
|
struct hifc_parent_sq_info_s *sq_info = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct unf_rec_pld_s *rec_pld = NULL;
|
|
|
|
v_sqe_ts = &v_sqe->ts_sl;
|
|
v_sqe_ts->task_type = HIFC_SQE_ELS_CMND;
|
|
els_req = &v_sqe_ts->cont.t_els_gs;
|
|
|
|
sq_info = (struct hifc_parent_sq_info_s *)v_sq_info;
|
|
hba = (struct hifc_hba_s *)sq_info->phba;
|
|
|
|
/*
|
|
* When the PLOGI request is sent, the microcode needs to be instructed
|
|
* to clear the I/O related to the link to avoid data inconsistency
|
|
* caused by the disorder of the IO.
|
|
*/
|
|
if (((cmnd == ELS_LOGO) || (cmnd == ELS_PLOGI)) && hba) {
|
|
els_req->wd4.clr_io = 1;
|
|
els_req->wd6.reset_exch_start = hba->exit_base;
|
|
els_req->wd6.reset_exch_end = hba->exit_base +
|
|
(hba->exit_count - 1);
|
|
els_req->wd7.scqn = v_scqn;
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"Port(0x%x) Rport(0x%x) SID(0x%x) send %s to DID(0x%x), notify clean io start 0x%x, end 0x%x, scqn 0x%x.",
|
|
hba->port_cfg.port_id, sq_info->rport_index,
|
|
sq_info->local_port_id,
|
|
(cmnd == ELS_PLOGI) ? "PLOGI" : "LOGO",
|
|
sq_info->remote_port_id,
|
|
els_req->wd6.reset_exch_start,
|
|
els_req->wd6.reset_exch_end,
|
|
v_scqn);
|
|
|
|
return;
|
|
}
|
|
|
|
/* The chip updates the PLOGI ACC negotiation parameters. */
|
|
if (cmnd == ELS_PRLI) {
|
|
els_req->wd5.seq_cnt = sq_info->plogi_coparams.seq_cnt;
|
|
els_req->wd5.e_d_tov = sq_info->plogi_coparams.ed_tov;
|
|
els_req->wd5.tx_mfs = sq_info->plogi_coparams.tx_mfs;
|
|
els_req->e_d_tov_timer_val =
|
|
sq_info->plogi_coparams.ed_tov_timer_val;
|
|
|
|
els_req->wd4.rec_support = hba->port_cfg.tape_support ? 1 : 0;
|
|
els_req->wd4.para_update = 0x01;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_INFO,
|
|
"Port(0x%x) save rport index(0x%x) login parms,seqcnt:0x%x, e_d_tov:0x%x,txmfs:0x%x,e_d_tovtimerval:0x%x.",
|
|
sq_info->local_port_id, sq_info->rport_index,
|
|
els_req->wd5.seq_cnt, els_req->wd5.e_d_tov,
|
|
els_req->wd5.tx_mfs,
|
|
els_req->e_d_tov_timer_val);
|
|
}
|
|
|
|
if (cmnd == ELS_ECHO)
|
|
els_req->echo_flag = UNF_TRUE;
|
|
if (cmnd == ELS_REC) {
|
|
rec_pld = (struct unf_rec_pld_s *)v_frame_pld;
|
|
els_req->wd4.rec_flag = 1;
|
|
rec_pld->ox_id += hba->exit_base;
|
|
els_req->wd4.orign_oxid = rec_pld->ox_id;
|
|
|
|
HIFC_TRACE(UNF_EVTLOG_DRIVER_INFO, UNF_LOG_LOGIN_ATT, UNF_MAJOR,
|
|
"Port(0x%x) Rport(0x%x) SID(0x%x) send Rec to DID(0x%x), origin_oxid 0x%x",
|
|
hba->port_cfg.port_id, sq_info->rport_index,
|
|
sq_info->local_port_id,
|
|
sq_info->remote_port_id,
|
|
els_req->wd4.orign_oxid);
|
|
}
|
|
}
|
|
|
|
void hifc_build_els_wqe_ts_magic_num(struct hifcoe_sqe_s *v_sqe,
|
|
unsigned short els_cmnd_type,
|
|
unsigned int v_magic_num)
|
|
{
|
|
struct hifcoe_sqe_t_els_gs_s *els_req;
|
|
struct hifcoe_sqe_els_rsp_s *els_rsp;
|
|
|
|
if (els_cmnd_type == ELS_ACC || els_cmnd_type == ELS_RJT) {
|
|
els_rsp = &v_sqe->ts_sl.cont.els_rsp;
|
|
els_rsp->magic_num = v_magic_num;
|
|
} else {
|
|
els_req = &v_sqe->ts_sl.cont.t_els_gs;
|
|
els_req->magic_num = v_magic_num;
|
|
}
|
|
}
|
|
|
|
void hifc_build_gs_wqe_ts_req(struct hifcoe_sqe_s *v_sqe,
|
|
unsigned int magic_num)
|
|
{
|
|
struct hifcoe_sqe_ts_s *v_sqe_ts = NULL;
|
|
struct hifcoe_sqe_t_els_gs_s *gs_req = NULL;
|
|
|
|
v_sqe_ts = &v_sqe->ts_sl;
|
|
v_sqe_ts->task_type = HIFC_SQE_GS_CMND;
|
|
|
|
gs_req = &v_sqe_ts->cont.t_els_gs;
|
|
gs_req->magic_num = magic_num;
|
|
}
|
|
|
|
void hifc_build_bls_wqe_ts_req(struct hifcoe_sqe_s *v_sqe,
|
|
unsigned int abts_param,
|
|
unsigned int magic_num)
|
|
{
|
|
struct hifcoe_sqe_abts_s *abts_ts;
|
|
|
|
v_sqe->ts_sl.task_type = HIFC_SQE_BLS_CMND;
|
|
abts_ts = &v_sqe->ts_sl.cont.abts;
|
|
abts_ts->fh_parm_abts = abts_param;
|
|
abts_ts->magic_num = magic_num;
|
|
}
|
|
|
|
void hifc_build_service_wqe_root_ts(void *v_hba,
|
|
struct hifc_root_sqe_s *v_rt_sqe,
|
|
unsigned int rx_id, unsigned int rport_id,
|
|
unsigned int scq_num)
|
|
{
|
|
unsigned char data_cos = 0;
|
|
unsigned int port_id = 0;
|
|
unsigned int service_type = 0;
|
|
struct hifc_hba_s *hba = NULL;
|
|
struct hifc_parent_queue_info_s *parent_queue_info = NULL;
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
|
|
port_id = HIFC_GET_HBA_PORT_ID(hba);
|
|
service_type = HIFC_GET_SERVICE_TYPE(hba);
|
|
|
|
if (rport_id >= UNF_HIFC_MAXRPORT_NUM) {
|
|
data_cos = HIFC_GET_PACKET_COS(service_type);
|
|
} else {
|
|
parent_queue_info =
|
|
&hba->parent_queue_mgr->parent_queues[rport_id];
|
|
data_cos = parent_queue_info->queue_data_cos;
|
|
}
|
|
|
|
v_rt_sqe->task_section.fc_dw0.exch_id = rx_id;
|
|
v_rt_sqe->task_section.fc_dw0.host_id = 0;
|
|
v_rt_sqe->task_section.fc_dw0.port_id = port_id;
|
|
v_rt_sqe->task_section.fc_dw0.off_load = HIFC_NO_OFFLOAD;
|
|
v_rt_sqe->task_section.fc_dw3.rport_index = HIFC_LSW(rport_id);
|
|
v_rt_sqe->task_section.fc_dw3.scq_num = HIFC_LSW(scq_num);
|
|
v_rt_sqe->task_section.fc_dw4.service_type = UNF_GET_SHIFTMASK(
|
|
service_type, 0, 0x1f);
|
|
v_rt_sqe->task_section.fc_dw4.pkt_type = HIFC_GET_PACKET_TYPE(
|
|
service_type);
|
|
v_rt_sqe->task_section.fc_dw4.pkt_cos = data_cos;
|
|
}
|
|
|
|
void hifc_build_service_wqe_root_sge(struct hifc_root_sqe_s *v_rt_sqe,
|
|
void *v_buf_addr,
|
|
unsigned long long v_phy_addr,
|
|
unsigned int buf_len,
|
|
void *v_hba)
|
|
{
|
|
unsigned long long frame_phy_addr;
|
|
|
|
/* Enter the SGE and convert it to the big-endian mode. */
|
|
frame_phy_addr = v_phy_addr;
|
|
v_rt_sqe->sge.buf_addr_hi = HIFC_HIGH_32_BITS(frame_phy_addr);
|
|
v_rt_sqe->sge.buf_addr_lo = HIFC_LOW_32_BITS(frame_phy_addr);
|
|
v_rt_sqe->sge.wd0.buf_len = buf_len;
|
|
v_rt_sqe->sge.wd0.ext_flag = 0;
|
|
v_rt_sqe->sge.wd1.rsvd = 0;
|
|
hifc_cpu_to_big32(&v_rt_sqe->sge, sizeof(v_rt_sqe->sge));
|
|
|
|
/* Converting FC Frames into big Ends */
|
|
hifc_cpu_to_big32(v_buf_addr, buf_len);
|
|
}
|
|
|
|
void hifc_build_service_wqe_ctx_sge(struct hifc_root_sqe_s *v_rt_sqe,
|
|
unsigned long long v_ctxt_addr,
|
|
unsigned int buf_len)
|
|
{
|
|
/* The SGE is filled in and converted to the big-endian mode. */
|
|
v_rt_sqe->ctx_sge.buf_addr_hi = HIFC_HIGH_32_BITS(v_ctxt_addr);
|
|
v_rt_sqe->ctx_sge.buf_addr_lo = HIFC_LOW_32_BITS(v_ctxt_addr);
|
|
v_rt_sqe->ctx_sge.wd0.buf_len = buf_len;
|
|
v_rt_sqe->ctx_sge.wd0.ext_flag = 0;
|
|
v_rt_sqe->ctx_sge.wd1.rsvd = 0;
|
|
|
|
hifc_cpu_to_big32(&v_rt_sqe->ctx_sge, sizeof(v_rt_sqe->ctx_sge));
|
|
}
|
|
|
|
void hifc_build_els_wqe_root_offload(struct hifc_root_sqe_s *v_rt_sqe,
|
|
dma_addr_t ctxt_addr,
|
|
unsigned int xid)
|
|
{
|
|
/* update Task Section DW0.OFFLOAD */
|
|
v_rt_sqe->task_section.fc_dw0.off_load = HIFC_HAVE_OFFLOAD;
|
|
|
|
/* update Context GPA DW1~2 */
|
|
v_rt_sqe->task_section.fc_dw1.context_gpa_hi =
|
|
HIFC_HIGH_32_BITS(ctxt_addr);
|
|
v_rt_sqe->task_section.fc_dw2.context_gpa_lo =
|
|
HIFC_LOW_32_BITS(ctxt_addr);
|
|
|
|
/* fill Context DW4 */
|
|
v_rt_sqe->task_section.fc_dw4.parent_xid = xid;
|
|
v_rt_sqe->task_section.fc_dw4.csize = HIFC_CNTX_SIZE_T_256B;
|
|
|
|
/* The sqe of the offload request has two sge. The first is the packet,
|
|
* and the second is the ctx.
|
|
*/
|
|
v_rt_sqe->ctrl_section.ch.wd0.bdsl =
|
|
2 * HIFC_BYTES_TO_QW_NUM(sizeof(struct hifc_root_sge_s));
|
|
}
|
|
|
|
void hifc_build_service_wqe_ctrl_section(struct hifcoe_wqe_ctrl_s *v_wqe_cs,
|
|
unsigned int ts_size,
|
|
unsigned int bdsi)
|
|
{
|
|
v_wqe_cs->ch.wd0.bdsl = bdsi;
|
|
v_wqe_cs->ch.wd0.drv_sl = 0;
|
|
v_wqe_cs->ch.wd0.rsvd0 = 0;
|
|
v_wqe_cs->ch.wd0.wf = 0;
|
|
v_wqe_cs->ch.wd0.cf = 0;
|
|
v_wqe_cs->ch.wd0.tsl = ts_size;
|
|
v_wqe_cs->ch.wd0.va = 0;
|
|
v_wqe_cs->ch.wd0.df = 0;
|
|
v_wqe_cs->ch.wd0.cr = 1;
|
|
v_wqe_cs->ch.wd0.dif_sl = 0;
|
|
v_wqe_cs->ch.wd0.csl = 0;
|
|
/* divided by 8 */
|
|
v_wqe_cs->ch.wd0.ctrl_sl = HIFC_BYTES_TO_QW_NUM(sizeof(*v_wqe_cs));
|
|
v_wqe_cs->ch.wd0.owner = 0;
|
|
}
|
|
|
|
void hifc_build_wqe_owner_pmsn(struct hifcoe_wqe_ctrl_s *v_wqe_cs,
|
|
unsigned short owner,
|
|
unsigned short pmsn)
|
|
{
|
|
v_wqe_cs->qsf.wqe_sn = pmsn;
|
|
v_wqe_cs->qsf.dump_wqe_sn = v_wqe_cs->qsf.wqe_sn;
|
|
v_wqe_cs->ch.wd0.owner = (unsigned int)owner;
|
|
}
|
|
|
|
void hifc_convert_parent_wqe_to_big_endian(struct hifcoe_sqe_s *v_sqe)
|
|
{
|
|
if (likely((v_sqe->ts_sl.task_type != HIFCOE_TASK_T_TRESP) &&
|
|
(v_sqe->ts_sl.task_type != HIFCOE_TASK_T_TMF_RESP))) {
|
|
/*
|
|
* Convert Control Secton and Task Section to big-endian. Before
|
|
* the SGE enters the queue, the upper-layer driver converts the
|
|
* SGE and Task Section to the big-endian mode.
|
|
*/
|
|
hifc_cpu_to_big32(&v_sqe->ctrl_sl, sizeof(v_sqe->ctrl_sl));
|
|
hifc_cpu_to_big32(&v_sqe->ts_sl, sizeof(v_sqe->ts_sl));
|
|
} else {
|
|
/*
|
|
* The HIFCOE_TASK_T_TRESP may use the SGE as the Task Section
|
|
* to convert the entire SQE into a large end.
|
|
*/
|
|
hifc_cpu_to_big32(v_sqe, sizeof(struct hifcoe_sqe_tresp_s));
|
|
}
|
|
}
|
|
|
|
void hifc_convert_root_wqe_to_big_endian(struct hifc_root_sqe_s *v_sqe)
|
|
{
|
|
hifc_cpu_to_big32(&v_sqe->ctrl_section, sizeof(v_sqe->ctrl_section));
|
|
hifc_cpu_to_big32(&v_sqe->task_section, sizeof(v_sqe->task_section));
|
|
}
|
|
|
|
void hifc_build_cmdqe_common(union hifc_cmdqe_u *cmdqe,
|
|
enum hifcoe_task_type_e task_type,
|
|
unsigned short rx_id)
|
|
{
|
|
cmdqe->common.wd0.task_type = task_type;
|
|
cmdqe->common.wd0.rx_id = rx_id;
|
|
cmdqe->common.wd0.rsvd0 = 0;
|
|
}
|
|
|
|
#define HIFC_STANDARD_SIRT_ENABLE 1
|
|
#define HIFC_STANDARD_SIRT_DISABLE 0
|
|
#define HIFC_UNKNOWN_ID 0xFFFF
|
|
|
|
void hifc_build_icmnd_wqe_ts_header(struct unf_frame_pkg_s *v_pkg,
|
|
struct hifcoe_sqe_s *v_sqe,
|
|
unsigned char task_type,
|
|
unsigned short exit_base,
|
|
unsigned char v_port_idx)
|
|
{
|
|
v_sqe->ts_sl.local_xid = UNF_GET_OXID(v_pkg) + exit_base;
|
|
v_sqe->ts_sl.task_type = task_type;
|
|
v_sqe->ts_sl.wd0.conn_id =
|
|
(unsigned short)(v_pkg->private[PKG_PRIVATE_XCHG_RPORT_INDEX]);
|
|
|
|
v_sqe->ts_sl.wd0.remote_xid = HIFC_UNKNOWN_ID;
|
|
}
|
|
|
|
void hifc_build_icmnd_wqe_ts(void *v_hba, struct unf_frame_pkg_s *v_pkg,
|
|
struct hifcoe_sqe_ts_s *v_sqe_ts)
|
|
{
|
|
struct hifcoe_sqe_icmnd_s *icmd = &v_sqe_ts->cont.icmnd;
|
|
void *phy_add = NULL;
|
|
struct hifc_hba_s *hba = NULL;
|
|
|
|
hba = (struct hifc_hba_s *)v_hba;
|
|
v_sqe_ts->cdb_type = 0;
|
|
memcpy(icmd->fcp_cmnd_iu, v_pkg->fcp_cmnd,
|
|
sizeof(struct unf_fcp_cmnd_s));
|
|
|
|
icmd->magic_num = UNF_GETXCHGALLOCTIME(v_pkg);
|
|
|
|
if (v_pkg->unf_rsp_pload_bl.buffer_ptr) {
|
|
phy_add = (void *)v_pkg->unf_rsp_pload_bl.buf_dma_addr;
|
|
icmd->rsp_gpa_hi = HIFC_HIGH_32_BITS(phy_add);
|
|
icmd->rsp_gpa_lo = HIFC_LOW_32_BITS(phy_add);
|
|
} else {
|
|
HIFC_TRACE(UNF_EVTLOG_IO_ERR, UNF_LOG_IO_ATT, UNF_ERR,
|
|
"[err]INI Build WQE sense buffer should not be null,sid_did (0x%x_0x%x) oxid(0x%x) pkg type(0x%x) hot pool tag(0x%x).",
|
|
v_pkg->frame_head.csctl_sid,
|
|
v_pkg->frame_head.rctl_did,
|
|
UNF_GET_OXID(v_pkg),
|
|
v_pkg->type, UNF_GET_XCHG_TAG(v_pkg));
|
|
}
|
|
|
|
if (v_sqe_ts->task_type != HIFC_SQE_FCP_ITMF) {
|
|
icmd->info.tmf.w0.bs.reset_exch_start = hba->exit_base;
|
|
icmd->info.tmf.w0.bs.reset_exch_end = hba->exit_base +
|
|
hba->exit_count - 1;
|
|
|
|
icmd->info.tmf.w1.bs.reset_did = UNF_GET_DID(v_pkg);
|
|
/* delivers the marker status flag to the microcode. */
|
|
icmd->info.tmf.w1.bs.marker_sts = 1;
|
|
HIFC_GET_RESET_TYPE(UNF_GET_TASK_MGMT_FLAGS(
|
|
v_pkg->fcp_cmnd->control),
|
|
icmd->info.tmf.w1.bs.reset_type);
|
|
|
|
icmd->info.tmf.w2.bs.reset_sid = UNF_GET_SID(v_pkg);
|
|
|
|
memcpy(icmd->info.tmf.reset_lun, v_pkg->fcp_cmnd->lun,
|
|
sizeof(icmd->info.tmf.reset_lun));
|
|
}
|
|
}
|
|
|
|
void hifc_build_srq_wqe_ctrls(struct hifcoe_rqe_s *v_rqe,
|
|
unsigned short owner,
|
|
unsigned short pmsn)
|
|
{
|
|
struct hifcoe_wqe_ctrl_ch_s *wqe_ctrls = NULL;
|
|
|
|
wqe_ctrls = &v_rqe->ctrl_sl.ch;
|
|
wqe_ctrls->wd0.owner = owner;
|
|
wqe_ctrls->wd0.ctrl_sl = sizeof(struct hifcoe_wqe_ctrl_s) >> 3;
|
|
wqe_ctrls->wd0.csl = 1;
|
|
wqe_ctrls->wd0.dif_sl = 0;
|
|
wqe_ctrls->wd0.cr = 1;
|
|
wqe_ctrls->wd0.df = 0;
|
|
wqe_ctrls->wd0.va = 0;
|
|
wqe_ctrls->wd0.tsl = 0;
|
|
wqe_ctrls->wd0.cf = 0;
|
|
wqe_ctrls->wd0.wf = 0;
|
|
wqe_ctrls->wd0.drv_sl = sizeof(struct hifcoe_rqe_drv_s) >> 3;
|
|
wqe_ctrls->wd0.bdsl = sizeof(struct hifcoe_constant_sge_s) >> 3;
|
|
|
|
v_rqe->ctrl_sl.wd0.wqe_msn = pmsn;
|
|
v_rqe->ctrl_sl.wd0.dump_wqe_msn = v_rqe->ctrl_sl.wd0.wqe_msn;
|
|
}
|