Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: lpfc: Create a sysfs entry called lpfc_xcvr_data for transceiver info

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>

authored by

Justin Tee and committed by
Martin K. Petersen
479b0917 eaf660e4

+252 -2
+118
drivers/scsi/lpfc/lpfc_attr.c
··· 1877 1877 return 0; 1878 1878 } 1879 1879 1880 + static ssize_t 1881 + lpfc_xcvr_data_show(struct device *dev, struct device_attribute *attr, 1882 + char *buf) 1883 + { 1884 + struct Scsi_Host *shost = class_to_shost(dev); 1885 + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; 1886 + struct lpfc_hba *phba = vport->phba; 1887 + int rc; 1888 + int len = 0; 1889 + struct lpfc_rdp_context *rdp_context; 1890 + u16 temperature; 1891 + u16 rx_power; 1892 + u16 tx_bias; 1893 + u16 tx_power; 1894 + u16 vcc; 1895 + char chbuf[128]; 1896 + u16 wavelength = 0; 1897 + struct sff_trasnceiver_codes_byte7 *trasn_code_byte7; 1898 + 1899 + /* Get transceiver information */ 1900 + rdp_context = kmalloc(sizeof(*rdp_context), GFP_KERNEL); 1901 + 1902 + rc = lpfc_get_sfp_info_wait(phba, rdp_context); 1903 + if (rc) { 1904 + len = scnprintf(buf, PAGE_SIZE - len, "SFP info NA:\n"); 1905 + goto out_free_rdp; 1906 + } 1907 + 1908 + strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_NAME], 16); 1909 + chbuf[16] = 0; 1910 + 1911 + len = scnprintf(buf, PAGE_SIZE - len, "VendorName:\t%s\n", chbuf); 1912 + len += scnprintf(buf + len, PAGE_SIZE - len, 1913 + "VendorOUI:\t%02x-%02x-%02x\n", 1914 + (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI], 1915 + (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 1], 1916 + (uint8_t)rdp_context->page_a0[SSF_VENDOR_OUI + 2]); 1917 + strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_PN], 16); 1918 + chbuf[16] = 0; 1919 + len += scnprintf(buf + len, PAGE_SIZE - len, "VendorPN:\t%s\n", chbuf); 1920 + strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_SN], 16); 1921 + chbuf[16] = 0; 1922 + len += scnprintf(buf + len, PAGE_SIZE - len, "VendorSN:\t%s\n", chbuf); 1923 + strncpy(chbuf, &rdp_context->page_a0[SSF_VENDOR_REV], 4); 1924 + chbuf[4] = 0; 1925 + len += scnprintf(buf + len, PAGE_SIZE - len, "VendorRev:\t%s\n", chbuf); 1926 + strncpy(chbuf, &rdp_context->page_a0[SSF_DATE_CODE], 8); 1927 + chbuf[8] = 0; 1928 + len += scnprintf(buf + len, PAGE_SIZE - len, "DateCode:\t%s\n", chbuf); 1929 + len += scnprintf(buf + len, PAGE_SIZE - len, "Identifier:\t%xh\n", 1930 + (uint8_t)rdp_context->page_a0[SSF_IDENTIFIER]); 1931 + len += scnprintf(buf + len, PAGE_SIZE - len, "ExtIdentifier:\t%xh\n", 1932 + (uint8_t)rdp_context->page_a0[SSF_EXT_IDENTIFIER]); 1933 + len += scnprintf(buf + len, PAGE_SIZE - len, "Connector:\t%xh\n", 1934 + (uint8_t)rdp_context->page_a0[SSF_CONNECTOR]); 1935 + wavelength = (rdp_context->page_a0[SSF_WAVELENGTH_B1] << 8) | 1936 + rdp_context->page_a0[SSF_WAVELENGTH_B0]; 1937 + 1938 + len += scnprintf(buf + len, PAGE_SIZE - len, "Wavelength:\t%d nm\n", 1939 + wavelength); 1940 + trasn_code_byte7 = (struct sff_trasnceiver_codes_byte7 *) 1941 + &rdp_context->page_a0[SSF_TRANSCEIVER_CODE_B7]; 1942 + 1943 + len += scnprintf(buf + len, PAGE_SIZE - len, "Speeds: \t"); 1944 + if (*(uint8_t *)trasn_code_byte7 == 0) { 1945 + len += scnprintf(buf + len, PAGE_SIZE - len, 1946 + "Unknown\n"); 1947 + } else { 1948 + if (trasn_code_byte7->fc_sp_100MB) 1949 + len += scnprintf(buf + len, PAGE_SIZE - len, 1950 + "1 "); 1951 + if (trasn_code_byte7->fc_sp_200mb) 1952 + len += scnprintf(buf + len, PAGE_SIZE - len, 1953 + "2 "); 1954 + if (trasn_code_byte7->fc_sp_400MB) 1955 + len += scnprintf(buf + len, PAGE_SIZE - len, 1956 + "4 "); 1957 + if (trasn_code_byte7->fc_sp_800MB) 1958 + len += scnprintf(buf + len, PAGE_SIZE - len, 1959 + "8 "); 1960 + if (trasn_code_byte7->fc_sp_1600MB) 1961 + len += scnprintf(buf + len, PAGE_SIZE - len, 1962 + "16 "); 1963 + if (trasn_code_byte7->fc_sp_3200MB) 1964 + len += scnprintf(buf + len, PAGE_SIZE - len, 1965 + "32 "); 1966 + if (trasn_code_byte7->speed_chk_ecc) 1967 + len += scnprintf(buf + len, PAGE_SIZE - len, 1968 + "64 "); 1969 + len += scnprintf(buf + len, PAGE_SIZE - len, "GB\n"); 1970 + } 1971 + temperature = (rdp_context->page_a2[SFF_TEMPERATURE_B1] << 8 | 1972 + rdp_context->page_a2[SFF_TEMPERATURE_B0]); 1973 + vcc = (rdp_context->page_a2[SFF_VCC_B1] << 8 | 1974 + rdp_context->page_a2[SFF_VCC_B0]); 1975 + tx_power = (rdp_context->page_a2[SFF_TXPOWER_B1] << 8 | 1976 + rdp_context->page_a2[SFF_TXPOWER_B0]); 1977 + tx_bias = (rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 | 1978 + rdp_context->page_a2[SFF_TX_BIAS_CURRENT_B0]); 1979 + rx_power = (rdp_context->page_a2[SFF_RXPOWER_B1] << 8 | 1980 + rdp_context->page_a2[SFF_RXPOWER_B0]); 1981 + 1982 + len += scnprintf(buf + len, PAGE_SIZE - len, 1983 + "Temperature:\tx%04x C\n", temperature); 1984 + len += scnprintf(buf + len, PAGE_SIZE - len, "Vcc:\t\tx%04x V\n", vcc); 1985 + len += scnprintf(buf + len, PAGE_SIZE - len, 1986 + "TxBiasCurrent:\tx%04x mA\n", tx_bias); 1987 + len += scnprintf(buf + len, PAGE_SIZE - len, "TxPower:\tx%04x mW\n", 1988 + tx_power); 1989 + len += scnprintf(buf + len, PAGE_SIZE - len, "RxPower:\tx%04x mW\n", 1990 + rx_power); 1991 + out_free_rdp: 1992 + kfree(rdp_context); 1993 + return len; 1994 + } 1995 + 1880 1996 /** 1881 1997 * lpfc_board_mode_show - Return the state of the board 1882 1998 * @dev: class device that is converted into a Scsi_host. ··· 2926 2810 static DEVICE_ATTR_RO(lpfc_enable_fip); 2927 2811 static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, 2928 2812 lpfc_board_mode_show, lpfc_board_mode_store); 2813 + static DEVICE_ATTR_RO(lpfc_xcvr_data); 2929 2814 static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); 2930 2815 static DEVICE_ATTR(max_vpi, S_IRUGO, lpfc_max_vpi_show, NULL); 2931 2816 static DEVICE_ATTR(used_vpi, S_IRUGO, lpfc_used_vpi_show, NULL); ··· 6023 5906 &dev_attr_lpfc_fcp_wait_abts_rsp.attr, 6024 5907 &dev_attr_nport_evt_cnt.attr, 6025 5908 &dev_attr_board_mode.attr, 5909 + &dev_attr_lpfc_xcvr_data.attr, 6026 5910 &dev_attr_max_vpi.attr, 6027 5911 &dev_attr_used_vpi.attr, 6028 5912 &dev_attr_max_rpi.attr,
+3
drivers/scsi/lpfc/lpfc_crtn.h
··· 687 687 688 688 void lpfc_sli_rpi_release(struct lpfc_vport *vport, 689 689 struct lpfc_nodelist *ndlp); 690 + 691 + int lpfc_get_sfp_info_wait(struct lpfc_hba *phba, 692 + struct lpfc_rdp_context *rdp_context);
+128
drivers/scsi/lpfc/lpfc_els.c
··· 7190 7190 return 1; 7191 7191 } 7192 7192 7193 + int lpfc_get_sfp_info_wait(struct lpfc_hba *phba, 7194 + struct lpfc_rdp_context *rdp_context) 7195 + { 7196 + LPFC_MBOXQ_t *mbox = NULL; 7197 + int rc; 7198 + struct lpfc_dmabuf *mp; 7199 + struct lpfc_dmabuf *mpsave; 7200 + void *virt; 7201 + MAILBOX_t *mb; 7202 + 7203 + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); 7204 + if (!mbox) { 7205 + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS, 7206 + "7205 failed to allocate mailbox memory"); 7207 + return 1; 7208 + } 7209 + 7210 + if (lpfc_sli4_dump_page_a0(phba, mbox)) 7211 + goto sfp_fail; 7212 + mp = mbox->ctx_buf; 7213 + mpsave = mp; 7214 + virt = mp->virt; 7215 + if (phba->sli_rev < LPFC_SLI_REV4) { 7216 + mb = &mbox->u.mb; 7217 + mb->un.varDmp.cv = 1; 7218 + mb->un.varDmp.co = 1; 7219 + mb->un.varWords[2] = 0; 7220 + mb->un.varWords[3] = DMP_SFF_PAGE_A0_SIZE / 4; 7221 + mb->un.varWords[4] = 0; 7222 + mb->un.varWords[5] = 0; 7223 + mb->un.varWords[6] = 0; 7224 + mb->un.varWords[7] = 0; 7225 + mb->un.varWords[8] = 0; 7226 + mb->un.varWords[9] = 0; 7227 + mb->un.varWords[10] = 0; 7228 + mbox->in_ext_byte_len = DMP_SFF_PAGE_A0_SIZE; 7229 + mbox->out_ext_byte_len = DMP_SFF_PAGE_A0_SIZE; 7230 + mbox->mbox_offset_word = 5; 7231 + mbox->ctx_buf = virt; 7232 + } else { 7233 + bf_set(lpfc_mbx_memory_dump_type3_length, 7234 + &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE); 7235 + mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys); 7236 + mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys); 7237 + } 7238 + mbox->vport = phba->pport; 7239 + mbox->ctx_ndlp = (struct lpfc_rdp_context *)rdp_context; 7240 + 7241 + rc = lpfc_sli_issue_mbox_wait(phba, mbox, 30); 7242 + if (rc == MBX_NOT_FINISHED) { 7243 + rc = 1; 7244 + goto error; 7245 + } 7246 + 7247 + if (phba->sli_rev == LPFC_SLI_REV4) 7248 + mp = (struct lpfc_dmabuf *)(mbox->ctx_buf); 7249 + else 7250 + mp = mpsave; 7251 + 7252 + if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) { 7253 + rc = 1; 7254 + goto error; 7255 + } 7256 + 7257 + lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0, 7258 + DMP_SFF_PAGE_A0_SIZE); 7259 + 7260 + memset(mbox, 0, sizeof(*mbox)); 7261 + memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE); 7262 + INIT_LIST_HEAD(&mp->list); 7263 + 7264 + /* save address for completion */ 7265 + mbox->ctx_buf = mp; 7266 + mbox->vport = phba->pport; 7267 + 7268 + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY); 7269 + bf_set(lpfc_mbx_memory_dump_type3_type, 7270 + &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD); 7271 + bf_set(lpfc_mbx_memory_dump_type3_link, 7272 + &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port); 7273 + bf_set(lpfc_mbx_memory_dump_type3_page_no, 7274 + &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2); 7275 + if (phba->sli_rev < LPFC_SLI_REV4) { 7276 + mb = &mbox->u.mb; 7277 + mb->un.varDmp.cv = 1; 7278 + mb->un.varDmp.co = 1; 7279 + mb->un.varWords[2] = 0; 7280 + mb->un.varWords[3] = DMP_SFF_PAGE_A2_SIZE / 4; 7281 + mb->un.varWords[4] = 0; 7282 + mb->un.varWords[5] = 0; 7283 + mb->un.varWords[6] = 0; 7284 + mb->un.varWords[7] = 0; 7285 + mb->un.varWords[8] = 0; 7286 + mb->un.varWords[9] = 0; 7287 + mb->un.varWords[10] = 0; 7288 + mbox->in_ext_byte_len = DMP_SFF_PAGE_A2_SIZE; 7289 + mbox->out_ext_byte_len = DMP_SFF_PAGE_A2_SIZE; 7290 + mbox->mbox_offset_word = 5; 7291 + mbox->ctx_buf = virt; 7292 + } else { 7293 + bf_set(lpfc_mbx_memory_dump_type3_length, 7294 + &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE); 7295 + mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys); 7296 + mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys); 7297 + } 7298 + 7299 + mbox->ctx_ndlp = (struct lpfc_rdp_context *)rdp_context; 7300 + rc = lpfc_sli_issue_mbox_wait(phba, mbox, 30); 7301 + if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) { 7302 + rc = 1; 7303 + goto error; 7304 + } 7305 + rc = 0; 7306 + 7307 + lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2, 7308 + DMP_SFF_PAGE_A2_SIZE); 7309 + 7310 + error: 7311 + mbox->ctx_buf = mpsave; 7312 + lpfc_mbox_rsrc_cleanup(phba, mbox, MBOX_THD_UNLOCKED); 7313 + 7314 + return rc; 7315 + 7316 + sfp_fail: 7317 + mempool_free(mbox, phba->mbox_mem_pool); 7318 + return 1; 7319 + } 7320 + 7193 7321 /* 7194 7322 * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS. 7195 7323 * @vport: pointer to a host virtual N_Port data structure.
+3 -2
drivers/scsi/lpfc/lpfc_hw4.h
··· 3162 3162 #define SFF_LENGTH_COPPER 18 3163 3163 #define SSF_LENGTH_50UM_OM3 19 3164 3164 #define SSF_VENDOR_NAME 20 3165 - #define SSF_VENDOR_OUI 36 3165 + #define SSF_TRANSCEIVER2 36 3166 + #define SSF_VENDOR_OUI 37 3166 3167 #define SSF_VENDOR_PN 40 3167 3168 #define SSF_VENDOR_REV 56 3168 3169 #define SSF_WAVELENGTH_B1 60 ··· 3282 3281 3283 3282 struct sff_trasnceiver_codes_byte7 { 3284 3283 uint8_t fc_sp_100MB:1; /* 100 MB/sec */ 3285 - uint8_t reserve:1; 3284 + uint8_t speed_chk_ecc:1; 3286 3285 uint8_t fc_sp_200mb:1; /* 200 MB/sec */ 3287 3286 uint8_t fc_sp_3200MB:1; /* 3200 MB/sec */ 3288 3287 uint8_t fc_sp_400MB:1; /* 400 MB/sec */