diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index debba5e77d9cd040390e1c1c146b49878ae081a8..8a20b4e862241c7333e0eb927347816919e51a8f 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -648,6 +648,10 @@ struct lpfc_hba { #define HBA_FCP_IOQ_FLUSH 0x8000 /* FCP I/O queues being flushed */ #define HBA_FW_DUMP_OP 0x10000 /* Skips fn reset before FW dump */ #define HBA_RECOVERABLE_UE 0x20000 /* Firmware supports recoverable UE */ +#define HBA_FORCED_LINK_SPEED 0x40000 /* + * Firmware supports Forced Link Speed + * capability + */ uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/ struct lpfc_dmabuf slim2p; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 3740e5d168a2a98182e5964db2c4d0463a0b3790..c84775562c65e9b486eee549594c800c03945fd7 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3668,7 +3668,12 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr, int nolip = 0; const char *val_buf = buf; int err; - uint32_t prev_val; + uint32_t prev_val, if_type; + + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); + if (if_type == LPFC_SLI_INTF_IF_TYPE_2 && + phba->hba_flag & HBA_FORCED_LINK_SPEED) + return -EPERM; if (!strncmp(buf, "nolip ", strlen("nolip "))) { nolip = 1; diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 05dcc2abd541a2c8900a58f8bc1f494710e68b5e..e6a5254abd4fc0c3ff3e93834c0a9788521ad1e6 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -5185,6 +5185,48 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) return rc; } +static int +lpfc_forced_link_speed(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost = job->shost; + struct lpfc_vport *vport = shost_priv(shost); + struct lpfc_hba *phba = vport->phba; + struct forced_link_speed_support_reply *forced_reply; + int rc = 0; + + if (job->request_len < + sizeof(struct fc_bsg_request) + + sizeof(struct get_forced_link_speed_support)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "0048 Received FORCED_LINK_SPEED request " + "below minimum size\n"); + rc = -EINVAL; + goto job_error; + } + + forced_reply = (struct forced_link_speed_support_reply *) + job->reply->reply_data.vendor_reply.vendor_rsp; + + if (job->reply_len < + sizeof(struct fc_bsg_request) + + sizeof(struct forced_link_speed_support_reply)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "0049 Received FORCED_LINK_SPEED reply below " + "minimum size\n"); + rc = -EINVAL; + goto job_error; + } + + forced_reply->supported = (phba->hba_flag & HBA_FORCED_LINK_SPEED) + ? LPFC_FORCED_LINK_SPEED_SUPPORTED + : LPFC_FORCED_LINK_SPEED_NOT_SUPPORTED; +job_error: + job->reply->result = rc; + if (rc == 0) + job->job_done(job); + return rc; +} + /** * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job * @job: fc_bsg_job to handle @@ -5227,6 +5269,9 @@ lpfc_bsg_hst_vendor(struct fc_bsg_job *job) case LPFC_BSG_VENDOR_MENLO_DATA: rc = lpfc_menlo_cmd(job); break; + case LPFC_BSG_VENDOR_FORCED_LINK_SPEED: + rc = lpfc_forced_link_speed(job); + break; default: rc = -EINVAL; job->reply->reply_payload_rcv_len = 0; diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index e557bcdbcb198f2f63d2e45978d480fa0d54c3e7..f2247aa4fa17360f987d0b5fb0f671c5741618ad 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -35,6 +35,7 @@ #define LPFC_BSG_VENDOR_MENLO_DATA 9 #define LPFC_BSG_VENDOR_DIAG_MODE_END 10 #define LPFC_BSG_VENDOR_LINK_DIAG_TEST 11 +#define LPFC_BSG_VENDOR_FORCED_LINK_SPEED 14 struct set_ct_event { uint32_t command; @@ -284,6 +285,15 @@ struct lpfc_sli_config_mbox { } un; }; +#define LPFC_FORCED_LINK_SPEED_NOT_SUPPORTED 0 +#define LPFC_FORCED_LINK_SPEED_SUPPORTED 1 +struct get_forced_link_speed_support { + uint32_t command; +}; +struct forced_link_speed_support_reply { + uint8_t supported; +}; + /* driver only */ #define SLI_CONFIG_NOT_HANDLED 0 #define SLI_CONFIG_HANDLED 1 diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 0b2c3377fa0afcdeeedd887dca9dccf3c4bd435c..bbdcb5abcf564206795d91621b06af8fefa9cd05 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -2290,6 +2290,9 @@ struct lpfc_mbx_read_config { #define lpfc_mbx_rd_conf_r_a_tov_SHIFT 0 #define lpfc_mbx_rd_conf_r_a_tov_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_r_a_tov_WORD word6 +#define lpfc_mbx_rd_conf_link_speed_SHIFT 16 +#define lpfc_mbx_rd_conf_link_speed_MASK 0x0000FFFF +#define lpfc_mbx_rd_conf_link_speed_WORD word6 uint32_t rsvd_7; uint32_t rsvd_8; uint32_t word9; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 117c69a8278607fd13137d0b5f233b1b76a83d20..53227e5fd2fd71648ce724890f59fe34a3c12269 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -6931,6 +6931,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) struct lpfc_mbx_get_func_cfg *get_func_cfg; struct lpfc_rsrc_desc_fcfcoe *desc; char *pdesc_0; + uint16_t forced_link_speed; + uint32_t if_type; int length, i, rc = 0, rc2; pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -7024,6 +7026,58 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) if (rc) goto read_cfg_out; + /* Update link speed if forced link speed is supported */ + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); + if (if_type == LPFC_SLI_INTF_IF_TYPE_2) { + forced_link_speed = + bf_get(lpfc_mbx_rd_conf_link_speed, rd_config); + if (forced_link_speed) { + phba->hba_flag |= HBA_FORCED_LINK_SPEED; + + switch (forced_link_speed) { + case LINK_SPEED_1G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_1G; + break; + case LINK_SPEED_2G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_2G; + break; + case LINK_SPEED_4G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_4G; + break; + case LINK_SPEED_8G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_8G; + break; + case LINK_SPEED_10G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_10G; + break; + case LINK_SPEED_16G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_16G; + break; + case LINK_SPEED_32G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_32G; + break; + case 0xffff: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_AUTO; + break; + default: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "0047 Unrecognized link " + "speed : %d\n", + forced_link_speed); + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_AUTO; + } + } + } + /* Reset the DFT_HBA_Q_DEPTH to the max xri */ length = phba->sli4_hba.max_cfg_param.max_xri - lpfc_sli4_get_els_iocb_cnt(phba);