{
        struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
            struct device, kobj)));
-       struct qla_hw_data *ha = vha->hw;
-       uint16_t iter, addr, offset;
        int rval;
 
-       if (!capable(CAP_SYS_ADMIN) || off != 0 || count != SFP_DEV_SIZE * 2)
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count < SFP_DEV_SIZE)
                return 0;
 
-       if (ha->sfp_data)
-               goto do_read;
-
-       ha->sfp_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
-           &ha->sfp_data_dma);
-       if (!ha->sfp_data) {
-               ql_log(ql_log_warn, vha, 0x706c,
-                   "Unable to allocate memory for SFP read-data.\n");
+       if (qla2x00_reset_active(vha))
                return 0;
-       }
-
-do_read:
-       memset(ha->sfp_data, 0, SFP_BLOCK_SIZE);
-       addr = 0xa0;
-       for (iter = 0, offset = 0; iter < (SFP_DEV_SIZE * 2) / SFP_BLOCK_SIZE;
-           iter++, offset += SFP_BLOCK_SIZE) {
-               if (iter == 4) {
-                       /* Skip to next device address. */
-                       addr = 0xa2;
-                       offset = 0;
-               }
-
-               rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data,
-                   addr, offset, SFP_BLOCK_SIZE, BIT_1);
-               if (rval != QLA_SUCCESS) {
-                       ql_log(ql_log_warn, vha, 0x706d,
-                           "Unable to read SFP data (%x/%x/%x).\n", rval,
-                           addr, offset);
 
-                       return -EIO;
-               }
-               memcpy(buf, ha->sfp_data, SFP_BLOCK_SIZE);
-               buf += SFP_BLOCK_SIZE;
-       }
+       rval = qla2x00_read_sfp_dev(vha, buf, count);
+       if (rval)
+               return -EIO;
 
        return count;
 }
                .name = "sfp",
                .mode = S_IRUSR | S_IWUSR,
        },
-       .size = SFP_DEV_SIZE * 2,
+       .size = SFP_DEV_SIZE,
        .read = qla2x00_sysfs_read_sfp,
 };
 
 
                uint32_t        n2n_ae:1;
                uint32_t        fw_started:1;
                uint32_t        fw_init_done:1;
+
+               uint32_t        detected_lr_sfp:1;
+               uint32_t        using_lr_setting:1;
        } flags;
 
+       u8 long_range_distance; /* 32G & above */
+#define LR_DISTANCE_5K  1
+#define LR_DISTANCE_10K 0
+
        /* This spinlock is used to protect "io transactions", you must
        * acquire it before doing any IO to the card, eg with RD_REG*() and
        * WRT_REG*() for the duration of your entire commandtransaction.
        struct sns_cmd_pkt      *sns_cmd;
        dma_addr_t              sns_cmd_dma;
 
-#define SFP_DEV_SIZE    256
+#define SFP_DEV_SIZE    512
 #define SFP_BLOCK_SIZE  64
        void            *sfp_data;
        dma_addr_t      sfp_data_dma;
 #define FX00_HOST_INFO_RESEND  26
 #define QPAIR_ONLINE_CHECK_NEEDED      27
 #define SET_ZIO_THRESHOLD_NEEDED       28
+#define DETECT_SFP_CHANGE      29
 
        unsigned long   pci_flags;
 #define PFLG_DISCONNECTED      0       /* PCI device removed */
        WAIT_LUN,
 };
 
+/* Refer to SNIA SFF 8247 */
+struct sff_8247_a0 {
+       u8 txid;        /* transceiver id */
+       u8 ext_txid;
+       u8 connector;
+       /* compliance code */
+       u8 eth_infi_cc3;        /* ethernet, inifiband */
+       u8 sonet_cc4[2];
+       u8 eth_cc6;
+       /* link length */
+#define FC_LL_VL BIT_7 /* very long */
+#define FC_LL_S  BIT_6 /* Short */
+#define FC_LL_I  BIT_5 /* Intermidiate*/
+#define FC_LL_L  BIT_4 /* Long */
+#define FC_LL_M  BIT_3 /* Medium */
+#define FC_LL_SA BIT_2 /* ShortWave laser */
+#define FC_LL_LC BIT_1 /* LongWave laser */
+#define FC_LL_EL BIT_0 /* Electrical inter enclosure */
+       u8 fc_ll_cc7;
+       /* FC technology */
+#define FC_TEC_EL BIT_7        /* Electrical inter enclosure */
+#define FC_TEC_SN BIT_6        /* short wave w/o OFC */
+#define FC_TEC_SL BIT_5        /* short wave with OFC */
+#define FC_TEC_LL BIT_4        /* Longwave Laser */
+#define FC_TEC_ACT BIT_3       /* Active cable */
+#define FC_TEC_PAS BIT_2       /* Passive cable */
+       u8 fc_tec_cc8;
+       /* Transmission Media */
+#define FC_MED_TW BIT_7        /* Twin Ax */
+#define FC_MED_TP BIT_6        /* Twited Pair */
+#define FC_MED_MI BIT_5        /* Min Coax */
+#define FC_MED_TV BIT_4        /* Video Coax */
+#define FC_MED_M6 BIT_3        /* Multimode, 62.5um */
+#define FC_MED_M5 BIT_2        /* Multimode, 50um */
+#define FC_MED_SM BIT_0        /* Single Mode */
+       u8 fc_med_cc9;
+       /* speed FC_SP_12: 12*100M = 1200 MB/s */
+#define FC_SP_12 BIT_7
+#define FC_SP_8  BIT_6
+#define FC_SP_16 BIT_5
+#define FC_SP_4  BIT_4
+#define FC_SP_32 BIT_3
+#define FC_SP_2  BIT_2
+#define FC_SP_1  BIT_0
+       u8 fc_sp_cc10;
+       u8 encode;
+       u8 bitrate;
+       u8 rate_id;
+       u8 length_km;           /* offset 14/eh */
+       u8 length_100m;
+       u8 length_50um_10m;
+       u8 length_62um_10m;
+       u8 length_om4_10m;
+       u8 length_om3_10m;
+#define SFF_VEN_NAME_LEN 16
+       u8 vendor_name[SFF_VEN_NAME_LEN];       /* offset 20/14h */
+       u8 tx_compat;
+       u8 vendor_oui[3];
+#define SFF_PART_NAME_LEN 16
+       u8 vendor_pn[SFF_PART_NAME_LEN];        /* part number */
+       u8 vendor_rev[4];
+       u8 wavelength[2];
+       u8 resv;
+       u8 cc_base;
+       u8 options[2];  /* offset 64 */
+       u8 br_max;
+       u8 br_min;
+       u8 vendor_sn[16];
+       u8 date_code[8];
+       u8 diag;
+       u8 enh_options;
+       u8 sff_revision;
+       u8 cc_ext;
+       u8 vendor_specific[32];
+       u8 resv2[128];
+};
+
+#define AUTO_DETECT_SFP_SUPPORT(_vha)\
+       (ql2xautodetectsfp && !_vha->vp_idx &&          \
+       (IS_QLA25XX(_vha->hw) || IS_QLA81XX(_vha->hw) ||\
+       IS_QLA83XX(_vha->hw) || IS_QLA27XX(_vha->hw)))
+
 #define USER_CTRL_IRQ(_ha) (ql2xuctrlirq && QLA_TGT_MODE_ENABLED() && \
        (IS_QLA27XX(_ha) || IS_QLA83XX(_ha)))
 
 
 int qla24xx_post_newsess_work(struct scsi_qla_host *, port_id_t *, u8 *,
     void *);
 int qla24xx_fcport_handle_login(struct scsi_qla_host *, fc_port_t *);
+int qla24xx_detect_sfp(scsi_qla_host_t *vha);
 
 /*
  * Global Data in qla_os.c source file.
 extern int ql2xmvasynctoatio;
 extern int ql2xuctrlirq;
 extern int ql2xnvmeenable;
+extern int ql2xautodetectsfp;
 
 extern int qla2x00_loop_reset(scsi_qla_host_t *);
 extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
 extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
 extern int qla82xx_read_temperature(scsi_qla_host_t *);
 extern int qla8044_read_temperature(scsi_qla_host_t *);
+extern int qla2x00_read_sfp_dev(struct scsi_qla_host *, char *, int);
 
 /* BSG related functions */
 extern int qla24xx_bsg_request(struct bsg_job *);
 
        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() - Load and start RISC firmware.
  * @ha: HA context
                        rval = qla2x00_execute_fw(vha, srisc_address);
                        /* Retrieve firmware information. */
                        if (rval == QLA_SUCCESS) {
+                               qla24xx_detect_sfp(vha);
+
                                rval = qla2x00_set_exlogins_buffer(vha);
                                if (rval != QLA_SUCCESS)
                                        goto failed;
 
 
                vha->flags.management_server_logged_in = 0;
                qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
+
+               if (AUTO_DETECT_SFP_SUPPORT(vha)) {
+                       set_bit(DETECT_SFP_CHANGE, &vha->dpc_flags);
+                       qla2xxx_wake_dpc(vha);
+               }
                break;
 
        case MBA_LOOP_DOWN:             /* Loop Down Event */
 
        { MBC_INITIALIZE_MULTIQ },
        { MBC_IOCB_COMMAND_A64 },
        { MBC_GET_ADAPTER_LOOP_ID },
+       { MBC_READ_SFP },
 };
 
 static int is_rom_cmd(uint16_t cmd)
                mcp->mb[1] = MSW(risc_addr);
                mcp->mb[2] = LSW(risc_addr);
                mcp->mb[3] = 0;
+               mcp->mb[4] = 0;
                if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
                    IS_QLA27XX(ha)) {
-                       struct nvram_81xx *nv = ha->nvram;
-                       mcp->mb[4] = (nv->enhanced_features &
-                           EXTENDED_BB_CREDITS);
-               } else
-                       mcp->mb[4] = 0;
+                       if (ql2xautodetectsfp) {
+                               if (ha->flags.detected_lr_sfp) {
+                                       mcp->mb[4] |= EXTENDED_BB_CREDITS;
+                                       if (IS_QLA27XX(ha))
+                                               mcp->mb[4] |=
+                                       (u16)ha->long_range_distance << 12;
+                                       ha->flags.using_lr_setting = 1;
+                               }
+                       } else {
+                               struct nvram_81xx *nv = ha->nvram;
+
+                               if (nv->enhanced_features &
+                                   EXTENDED_BB_CREDITS) {
+                                       mcp->mb[4] |= EXTENDED_BB_CREDITS;
+                                       ha->flags.using_lr_setting = 1;
+                               }
+                       }
+               } else {
+                       ha->flags.using_lr_setting = 0;
+               }
 
                if (ql2xnvmeenable && IS_QLA27XX(ha))
                        mcp->mb[4] |= NVME_ENABLE_FLAG;
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x10e9,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+               if (mcp->mb[0] == MBS_COMMAND_ERROR &&
+                   mcp->mb[1] == 0x22)
+                       /* sfp is not there */
+                       rval = QLA_INTERFACE_ERROR;
        } else {
                ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea,
                    "Done %s.\n", __func__);
 
        return rval;
 }
+
+int
+qla2x00_read_sfp_dev(struct scsi_qla_host *vha, char *buf, int count)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint16_t iter, addr, offset;
+       dma_addr_t phys_addr;
+       int rval, c;
+       u8 *sfp_data;
+
+       memset(ha->sfp_data, 0, SFP_DEV_SIZE);
+       addr = 0xa0;
+       phys_addr = ha->sfp_data_dma;
+       sfp_data = ha->sfp_data;
+       offset = c = 0;
+
+       for (iter = 0; iter < SFP_DEV_SIZE / SFP_BLOCK_SIZE; iter++) {
+               if (iter == 4) {
+                       /* Skip to next device address. */
+                       addr = 0xa2;
+                       offset = 0;
+               }
+
+               rval = qla2x00_read_sfp(vha, phys_addr, sfp_data,
+                   addr, offset, SFP_BLOCK_SIZE, BIT_1);
+               if (rval != QLA_SUCCESS) {
+                       ql_log(ql_log_warn, vha, 0x706d,
+                           "Unable to read SFP data (%x/%x/%x).\n", rval,
+                           addr, offset);
+
+                       return rval;
+               }
+
+               if (buf && (c < count)) {
+                       u16 sz;
+
+                       if ((count - c) >= SFP_BLOCK_SIZE)
+                               sz = SFP_BLOCK_SIZE;
+                       else
+                               sz = count - c;
+
+                       memcpy(buf, sfp_data, sz);
+                       buf += SFP_BLOCK_SIZE;
+                       c += sz;
+               }
+               phys_addr += SFP_BLOCK_SIZE;
+               sfp_data  += SFP_BLOCK_SIZE;
+               offset += SFP_BLOCK_SIZE;
+       }
+
+       return rval;
+}
 
                "0 (Default). Do not move IOCBs"
                "1 - Move IOCBs.");
 
+int ql2xautodetectsfp = 1;
+module_param(ql2xautodetectsfp, int, 0444);
+MODULE_PARM_DESC(ql2xautodetectsfp,
+                "Detect SFP range and set appropriate distance.\n"
+                "1 (Default): Enable\n");
+
 /*
  * SCSI host template entry points
  */
        if (test_bit(UNLOADING, &base_vha->dpc_flags))
                return -ENODEV;
 
+       if (ha->flags.detected_lr_sfp) {
+               ql_log(ql_log_info, base_vha, 0xffff,
+                   "Reset chip to pick up LR SFP setting\n");
+               set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+               qla2xxx_wake_dpc(base_vha);
+       }
+
        return 0;
 
 probe_init_failed:
                    "loop_id_map=%p.\n", ha->loop_id_map);
        }
 
+       ha->sfp_data = dma_alloc_coherent(&ha->pdev->dev,
+           SFP_DEV_SIZE, &ha->sfp_data_dma, GFP_KERNEL);
+       if (!ha->sfp_data) {
+               ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
+                   "Unable to allocate memory for SFP read-data.\n");
+               goto fail_sfp_data;
+       }
+
        return 0;
 
+fail_sfp_data:
+       kfree(ha->loop_id_map);
 fail_loop_id_map:
        dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
 fail_async_pd:
                ha->ct_sns, ha->ct_sns_dma);
 
        if (ha->sfp_data)
-               dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
+               dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE, ha->sfp_data,
+                   ha->sfp_data_dma);
 
        if (ha->ms_iocb)
                dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
                        }
                }
 
+               if (test_and_clear_bit(DETECT_SFP_CHANGE,
+                       &base_vha->dpc_flags) &&
+                   !test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) {
+                       qla24xx_detect_sfp(base_vha);
+
+                       if (ha->flags.detected_lr_sfp !=
+                           ha->flags.using_lr_setting)
+                               set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+               }
+
                if (test_and_clear_bit(ISP_ABORT_NEEDED,
                                                &base_vha->dpc_flags)) {