scsi: lpfc: Create a sysfs entry called lpfc_xcvr_data for transceiver info
authorJustin Tee <justin.tee@broadcom.com>
Mon, 17 Oct 2022 16:43:22 +0000 (09:43 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Sat, 22 Oct 2022 03:19:15 +0000 (03:19 +0000)
The DUMP_MEMORY mailbox command is implemented for page A0 and A2 to
retrieve transceiver information from firmware.

The mailbox command output is then formatted to print raw data values for
userspace to parse via sysfs.

Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Link: https://lore.kernel.org/r/20221017164323.14536-4-justintee8345@gmail.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hw4.h

index ef1481326fd7d37359f578a88f9c2ad8fc495533..030ad1d59cbd662513422f7fd3981a9774001ea8 100644 (file)
@@ -1877,6 +1877,122 @@ lpfc_set_trunking(struct lpfc_hba *phba, char *buff_out)
        return 0;
 }
 
+static ssize_t
+lpfc_xcvr_data_show(struct device *dev, struct device_attribute *attr,
+                   char *buf)
+{
+       struct Scsi_Host  *shost = class_to_shost(dev);
+       struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+       struct lpfc_hba   *phba = vport->phba;
+       int rc;
+       int len = 0;
+       struct lpfc_rdp_context *rdp_context;
+       u16 temperature;
+       u16 rx_power;
+       u16 tx_bias;
+       u16 tx_power;
+       u16 vcc;
+       char chbuf[128];
+       u16 wavelength = 0;
+       struct sff_trasnceiver_codes_byte7 *trasn_code_byte7;
+
+       /* Get transceiver information */
+       rdp_context = kmalloc(sizeof(*rdp_context), GFP_KERNEL);
+
+       rc = lpfc_get_sfp_info_wait(phba, rdp_context);
+       if (rc) {
+               len = scnprintf(buf, PAGE_SIZE - len, "SFP info NA:\n");
+               goto out_free_rdp;
+       }
+
+       strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_NAME], 16);
+       chbuf[16] = 0;
+
+       len = scnprintf(buf, PAGE_SIZE - len, "VendorName:\t%s\n", chbuf);
+       len += scnprintf(buf + len, PAGE_SIZE - len,
+                        "VendorOUI:\t%02x-%02x-%02x\n",
+                        (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI],
+                        (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 1],
+                        (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 2]);
+       strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_PN], 16);
+       chbuf[16] = 0;
+       len += scnprintf(buf + len, PAGE_SIZE - len, "VendorPN:\t%s\n", chbuf);
+       strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_SN], 16);
+       chbuf[16] = 0;
+       len += scnprintf(buf + len, PAGE_SIZE - len, "VendorSN:\t%s\n", chbuf);
+       strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_REV], 4);
+       chbuf[4] = 0;
+       len += scnprintf(buf + len, PAGE_SIZE - len, "VendorRev:\t%s\n", chbuf);
+       strncpy(chbuf, &rdp_context->page_a0[SSF_DATE_CODE], 8);
+       chbuf[8] = 0;
+       len += scnprintf(buf + len, PAGE_SIZE - len, "DateCode:\t%s\n", chbuf);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "Identifier:\t%xh\n",
+                        (uint8_t)rdp_context->page_a0[SSF_IDENTIFIER]);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "ExtIdentifier:\t%xh\n",
+                        (uint8_t)rdp_context->page_a0[SSF_EXT_IDENTIFIER]);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "Connector:\t%xh\n",
+                        (uint8_t)rdp_context->page_a0[SSF_CONNECTOR]);
+       wavelength = (rdp_context->page_a0[SSF_WAVELENGTH_B1] << 8) |
+                     rdp_context->page_a0[SSF_WAVELENGTH_B0];
+
+       len += scnprintf(buf + len, PAGE_SIZE - len, "Wavelength:\t%d nm\n",
+                        wavelength);
+       trasn_code_byte7 = (struct sff_trasnceiver_codes_byte7 *)
+                       &rdp_context->page_a0[SSF_TRANSCEIVER_CODE_B7];
+
+       len += scnprintf(buf + len, PAGE_SIZE - len, "Speeds: \t");
+               if (*(uint8_t *)trasn_code_byte7 == 0) {
+                       len += scnprintf(buf + len, PAGE_SIZE - len,
+                                        "Unknown\n");
+               } else {
+                       if (trasn_code_byte7->fc_sp_100MB)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "1 ");
+                       if (trasn_code_byte7->fc_sp_200mb)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "2 ");
+                       if (trasn_code_byte7->fc_sp_400MB)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "4 ");
+                       if (trasn_code_byte7->fc_sp_800MB)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "8 ");
+                       if (trasn_code_byte7->fc_sp_1600MB)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "16 ");
+                       if (trasn_code_byte7->fc_sp_3200MB)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "32 ");
+                       if (trasn_code_byte7->speed_chk_ecc)
+                               len += scnprintf(buf + len, PAGE_SIZE - len,
+                                                "64 ");
+                       len += scnprintf(buf + len, PAGE_SIZE - len, "GB\n");
+               }
+       temperature = (rdp_context->page_a2[SFF_TEMPERATURE_B1] << 8 |
+                      rdp_context->page_a2[SFF_TEMPERATURE_B0]);
+       vcc = (rdp_context->page_a2[SFF_VCC_B1] << 8 |
+              rdp_context->page_a2[SFF_VCC_B0]);
+       tx_power = (rdp_context->page_a2[SFF_TXPOWER_B1] << 8 |
+                   rdp_context->page_a2[SFF_TXPOWER_B0]);
+       tx_bias = (rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+                  rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B0]);
+       rx_power = (rdp_context->page_a2[SFF_RXPOWER_B1] << 8 |
+                   rdp_context->page_a2[SFF_RXPOWER_B0]);
+
+       len += scnprintf(buf + len, PAGE_SIZE - len,
+                        "Temperature:\tx%04x C\n", temperature);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "Vcc:\t\tx%04x V\n", vcc);
+       len += scnprintf(buf + len, PAGE_SIZE - len,
+                        "TxBiasCurrent:\tx%04x mA\n", tx_bias);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "TxPower:\tx%04x mW\n",
+                        tx_power);
+       len += scnprintf(buf + len, PAGE_SIZE - len, "RxPower:\tx%04x mW\n",
+                        rx_power);
+out_free_rdp:
+       kfree(rdp_context);
+       return len;
+}
+
 /**
  * lpfc_board_mode_show - Return the state of the board
  * @dev: class device that is converted into a Scsi_host.
@@ -2810,6 +2926,7 @@ static DEVICE_ATTR_RO(lpfc_drvr_version);
 static DEVICE_ATTR_RO(lpfc_enable_fip);
 static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
                   lpfc_board_mode_show, lpfc_board_mode_store);
+static DEVICE_ATTR_RO(lpfc_xcvr_data);
 static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
 static DEVICE_ATTR(max_vpi, S_IRUGO, lpfc_max_vpi_show, NULL);
 static DEVICE_ATTR(used_vpi, S_IRUGO, lpfc_used_vpi_show, NULL);
@@ -5906,6 +6023,7 @@ static struct attribute *lpfc_hba_attrs[] = {
        &dev_attr_lpfc_fcp_wait_abts_rsp.attr,
        &dev_attr_nport_evt_cnt.attr,
        &dev_attr_board_mode.attr,
+       &dev_attr_lpfc_xcvr_data.attr,
        &dev_attr_max_vpi.attr,
        &dev_attr_used_vpi.attr,
        &dev_attr_max_rpi.attr,
index d2d207791056ca1b5938969116f96426bb5ba5cc..8928f016d09e2b72b6c05b0bc879343cf56b9833 100644 (file)
@@ -687,3 +687,6 @@ int lpfc_issue_els_qfpa(struct lpfc_vport *vport);
 
 void lpfc_sli_rpi_release(struct lpfc_vport *vport,
                          struct lpfc_nodelist *ndlp);
+
+int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
+                          struct lpfc_rdp_context *rdp_context);
index 863b2125fed6cebe9a0ffdade153914af88c8a26..2b03210264bb0a67e0fec5312b92c4fb125777e6 100644 (file)
@@ -7190,6 +7190,134 @@ rdp_fail:
        return 1;
 }
 
+int lpfc_get_sfp_info_wait(struct lpfc_hba *phba,
+                          struct lpfc_rdp_context *rdp_context)
+{
+       LPFC_MBOXQ_t *mbox = NULL;
+       int rc;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_dmabuf *mpsave;
+       void *virt;
+       MAILBOX_t *mb;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+                               "7205 failed to allocate mailbox memory");
+               return 1;
+       }
+
+       if (lpfc_sli4_dump_page_a0(phba, mbox))
+               goto sfp_fail;
+       mp = mbox->ctx_buf;
+       mpsave = mp;
+       virt = mp->virt;
+       if (phba->sli_rev < LPFC_SLI_REV4) {
+               mb = &mbox->u.mb;
+               mb->un.varDmp.cv = 1;
+               mb->un.varDmp.co = 1;
+               mb->un.varWords[2] = 0;
+               mb->un.varWords[3] = DMP_SFF_PAGE_A0_SIZE / 4;
+               mb->un.varWords[4] = 0;
+               mb->un.varWords[5] = 0;
+               mb->un.varWords[6] = 0;
+               mb->un.varWords[7] = 0;
+               mb->un.varWords[8] = 0;
+               mb->un.varWords[9] = 0;
+               mb->un.varWords[10] = 0;
+               mbox->in_ext_byte_len = DMP_SFF_PAGE_A0_SIZE;
+               mbox->out_ext_byte_len = DMP_SFF_PAGE_A0_SIZE;
+               mbox->mbox_offset_word = 5;
+               mbox->ctx_buf = virt;
+       } else {
+               bf_set(lpfc_mbx_memory_dump_type3_length,
+                      &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+               mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+               mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+       }
+       mbox->vport = phba->pport;
+       mbox->ctx_ndlp = (struct lpfc_rdp_context *)rdp_context;
+
+       rc = lpfc_sli_issue_mbox_wait(phba, mbox, 30);
+       if (rc == MBX_NOT_FINISHED) {
+               rc = 1;
+               goto error;
+       }
+
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               mp = (struct lpfc_dmabuf *)(mbox->ctx_buf);
+       else
+               mp = mpsave;
+
+       if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) {
+               rc = 1;
+               goto error;
+       }
+
+       lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+                            DMP_SFF_PAGE_A0_SIZE);
+
+       memset(mbox, 0, sizeof(*mbox));
+       memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+       INIT_LIST_HEAD(&mp->list);
+
+       /* save address for completion */
+       mbox->ctx_buf = mp;
+       mbox->vport = phba->pport;
+
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+       bf_set(lpfc_mbx_memory_dump_type3_type,
+              &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+       bf_set(lpfc_mbx_memory_dump_type3_link,
+              &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+       bf_set(lpfc_mbx_memory_dump_type3_page_no,
+              &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+       if (phba->sli_rev < LPFC_SLI_REV4) {
+               mb = &mbox->u.mb;
+               mb->un.varDmp.cv = 1;
+               mb->un.varDmp.co = 1;
+               mb->un.varWords[2] = 0;
+               mb->un.varWords[3] = DMP_SFF_PAGE_A2_SIZE / 4;
+               mb->un.varWords[4] = 0;
+               mb->un.varWords[5] = 0;
+               mb->un.varWords[6] = 0;
+               mb->un.varWords[7] = 0;
+               mb->un.varWords[8] = 0;
+               mb->un.varWords[9] = 0;
+               mb->un.varWords[10] = 0;
+               mbox->in_ext_byte_len = DMP_SFF_PAGE_A2_SIZE;
+               mbox->out_ext_byte_len = DMP_SFF_PAGE_A2_SIZE;
+               mbox->mbox_offset_word = 5;
+               mbox->ctx_buf = virt;
+       } else {
+               bf_set(lpfc_mbx_memory_dump_type3_length,
+                      &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+               mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+               mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+       }
+
+       mbox->ctx_ndlp = (struct lpfc_rdp_context *)rdp_context;
+       rc = lpfc_sli_issue_mbox_wait(phba, mbox, 30);
+       if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) {
+               rc = 1;
+               goto error;
+       }
+       rc = 0;
+
+       lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+                            DMP_SFF_PAGE_A2_SIZE);
+
+error:
+       mbox->ctx_buf = mpsave;
+       lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED);
+
+       return rc;
+
+sfp_fail:
+       mempool_free(mbox, phba->mbox_mem_pool);
+       return 1;
+}
+
 /*
  * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
  * @vport: pointer to a host virtual N_Port data structure.
index 5288fc69908a6691e5f65ad94ee16b3fbc61d41c..fb3504dbb8993d80c4cdb19ea137abe335d6d723 100644 (file)
@@ -3162,7 +3162,8 @@ struct lpfc_mbx_memory_dump_type3 {
 #define SFF_LENGTH_COPPER              18
 #define SSF_LENGTH_50UM_OM3            19
 #define SSF_VENDOR_NAME                        20
-#define SSF_VENDOR_OUI                 36
+#define SSF_TRANSCEIVER2               36
+#define SSF_VENDOR_OUI                 37
 #define SSF_VENDOR_PN                  40
 #define SSF_VENDOR_REV                 56
 #define SSF_WAVELENGTH_B1              60
@@ -3281,7 +3282,7 @@ struct sff_trasnceiver_codes_byte6 {
 
 struct sff_trasnceiver_codes_byte7 {
        uint8_t fc_sp_100MB:1;   /*  100 MB/sec */
-       uint8_t reserve:1;
+       uint8_t speed_chk_ecc:1;
        uint8_t fc_sp_200mb:1;   /*  200 MB/sec */
        uint8_t fc_sp_3200MB:1;  /* 3200 MB/sec */
        uint8_t fc_sp_400MB:1;   /*  400 MB/sec */