staging/rdma/hfi1: Tune for unknown channel if configuration file is absent
authorEaswar Hariharan <easwar.hariharan@intel.com>
Tue, 9 Feb 2016 22:29:22 +0000 (14:29 -0800)
committerDoug Ledford <dledford@redhat.com>
Fri, 11 Mar 2016 01:38:06 +0000 (20:38 -0500)
Currently, the driver fails to tune the SerDes and therefore prevents
link up if the configuration file is missing or fails parsing or
validation. This patch adds a fallback option so that the 8051 is asked
to tune for an unknown channel and possibly get the link up if tuning
succeeds. It also adds a user-friendly message to update the
configuration file if it is out-of-date.

Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Reviewed-by: Dean Luick <dean.luick@intel.com>
Signed-off-by: Easwar Hariharan <easwar.hariharan@intel.com>
Signed-off-by: Jubin John <jubin.john@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/staging/rdma/hfi1/chip.h
drivers/staging/rdma/hfi1/firmware.c
drivers/staging/rdma/hfi1/platform.c
drivers/staging/rdma/hfi1/qsfp.c

index 3b041dc771cdaaa95bbea4d5aeee65fd10b64038..b86c220161e59a7d0de3dc1834e4efc6d3cb0060 100644 (file)
 #define LINK_QUALITY_INFO            0x14
 #define REMOTE_DEVICE_ID            0x15
 
+/* 8051 lane specific register field IDs */
+#define TX_EQ_SETTINGS         0x00
+#define CHANNEL_LOSS_SETTINGS  0x05
+
 /* Lane ID for general configuration registers */
 #define GENERAL_CONFIG 4
 
index d2ec6c5f18ac2d13f2c720624b9f624268f972d7..35084b754b7c0cb53a2cdecbba7fd2097d29e336 100644 (file)
@@ -703,8 +703,6 @@ static int obtain_firmware(struct hfi1_devdata *dd)
                                                &dd->pcidev->dev);
                if (err) {
                        platform_config = NULL;
-                       fw_state = FW_ERR;
-                       fw_err = -ENOENT;
                        goto done;
                }
                dd->platform_config.data = platform_config->data;
@@ -1470,12 +1468,51 @@ int hfi1_firmware_init(struct hfi1_devdata *dd)
        return obtain_firmware(dd);
 }
 
+/*
+ * This function is a helper function for parse_platform_config(...) and
+ * does not check for validity of the platform configuration cache
+ * (because we know it is invalid as we are building up the cache).
+ * As such, this should not be called from anywhere other than
+ * parse_platform_config
+ */
+static int check_meta_version(struct hfi1_devdata *dd, u32 *system_table)
+{
+       u32 meta_ver, meta_ver_meta, ver_start, ver_len, mask;
+       struct platform_config_cache *pcfgcache = &dd->pcfg_cache;
+
+       if (!system_table)
+               return -EINVAL;
+
+       meta_ver_meta =
+       *(pcfgcache->config_tables[PLATFORM_CONFIG_SYSTEM_TABLE].table_metadata
+       + SYSTEM_TABLE_META_VERSION);
+
+       mask = ((1 << METADATA_TABLE_FIELD_START_LEN_BITS) - 1);
+       ver_start = meta_ver_meta & mask;
+
+       meta_ver_meta >>= METADATA_TABLE_FIELD_LEN_SHIFT;
+
+       mask = ((1 << METADATA_TABLE_FIELD_LEN_LEN_BITS) - 1);
+       ver_len = meta_ver_meta & mask;
+
+       ver_start /= 8;
+       meta_ver = *((u8 *)system_table + ver_start) & ((1 << ver_len) - 1);
+
+       if (meta_ver < 5) {
+               dd_dev_info(
+                       dd, "%s:Please update platform config\n", __func__);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 int parse_platform_config(struct hfi1_devdata *dd)
 {
        struct platform_config_cache *pcfgcache = &dd->pcfg_cache;
        u32 *ptr = NULL;
        u32 header1 = 0, header2 = 0, magic_num = 0, crc = 0, file_length = 0;
        u32 record_idx = 0, table_type = 0, table_length_dwords = 0;
+       int ret = -EINVAL; /* assume failure */
 
        if (!dd->platform_config.data) {
                dd_dev_info(dd, "%s: Missing config file\n", __func__);
@@ -1499,7 +1536,8 @@ int parse_platform_config(struct hfi1_devdata *dd)
                            __func__);
                goto bail;
        } else if (file_length < dd->platform_config.size) {
-               dd_dev_info(dd, "%s:File claims to be smaller than read size\n",
+               dd_dev_info(dd,
+                           "%s:File claims to be smaller than read size, continuing\n",
                            __func__);
        }
        /* exactly equal, perfection */
@@ -1537,6 +1575,9 @@ int parse_platform_config(struct hfi1_devdata *dd)
                        case PLATFORM_CONFIG_SYSTEM_TABLE:
                                pcfgcache->config_tables[table_type].num_table =
                                                                        1;
+                               ret = check_meta_version(dd, ptr);
+                               if (ret)
+                                       goto bail;
                                break;
                        case PLATFORM_CONFIG_PORT_TABLE:
                                pcfgcache->config_tables[table_type].num_table =
@@ -1609,7 +1650,7 @@ int parse_platform_config(struct hfi1_devdata *dd)
        return 0;
 bail:
        memset(pcfgcache, 0, sizeof(struct platform_config_cache));
-       return -EINVAL;
+       return ret;
 }
 
 static int get_platform_fw_field_metadata(struct hfi1_devdata *dd, int table,
index 0309c523882381fa640a1652edf4e08b7d3570f4..2f07becb0b96efe8fc3d6c4d9a19f0f44297b76f 100644 (file)
@@ -498,14 +498,14 @@ static void apply_rx_amplitude_settings(
 
 #define OPA_INVALID_INDEX 0xFFF
 
-static void apply_tx_lanes(struct hfi1_pportdata *ppd, u32 config_data,
-                          const char *message)
+static void apply_tx_lanes(struct hfi1_pportdata *ppd, u8 field_id,
+                          u32 config_data, const char *message)
 {
        u8 i;
        int ret = HCMD_SUCCESS;
 
        for (i = 0; i < 4; i++) {
-               ret = load_8051_config(ppd->dd, 0, i, config_data);
+               ret = load_8051_config(ppd->dd, field_id, i, config_data);
                if (ret != HCMD_SUCCESS) {
                        dd_dev_err(
                                ppd->dd,
@@ -524,6 +524,7 @@ static void apply_tunings(
        u8 precur = 0, attn = 0, postcur = 0, external_device_config = 0;
        u8 *cache = ppd->qsfp_info.cache;
 
+       /* Enable external device config if channel is limiting active */
        read_8051_config(ppd->dd, LINK_OPTIMIZATION_SETTINGS,
                         GENERAL_CONFIG, &config_data);
        config_data |= limiting_active;
@@ -536,6 +537,7 @@ static void apply_tunings(
                        __func__);
 
        config_data = 0; /* re-init  */
+       /* Pass tuning method to 8051 */
        read_8051_config(ppd->dd, LINK_TUNING_PARAMETERS, GENERAL_CONFIG,
                         &config_data);
        config_data |= tuning_method;
@@ -545,47 +547,39 @@ static void apply_tunings(
                dd_dev_err(ppd->dd, "%s: Failed to set tuning method\n",
                           __func__);
 
-       external_device_config =
-               ((cache[QSFP_MOD_PWR_OFFS] & 0x4) << 3) |
-               ((cache[QSFP_MOD_PWR_OFFS] & 0x8) << 2) |
-               ((cache[QSFP_EQ_INFO_OFFS] & 0x2) << 1) |
-               (cache[QSFP_EQ_INFO_OFFS] & 0x4);
-
-       config_data = 0; /* re-init  */
-       read_8051_config(ppd->dd, DC_HOST_COMM_SETTINGS, GENERAL_CONFIG,
-                        &config_data);
-       config_data |= (external_device_config << 24);
-       ret = load_8051_config(ppd->dd, DC_HOST_COMM_SETTINGS, GENERAL_CONFIG,
-                              config_data);
-       if (ret != HCMD_SUCCESS)
-               dd_dev_err(
-                       ppd->dd,
-                       "%s: Failed to set external device config parameters\n",
-                       __func__);
-
-       config_data = 0; /* re-init  */
-       read_8051_config(ppd->dd, TX_SETTINGS, GENERAL_CONFIG, &config_data);
-       if ((ppd->link_speed_supported & OPA_LINK_SPEED_25G) &&
-           (ppd->link_speed_enabled & OPA_LINK_SPEED_25G))
-               config_data |= 0x02;
-       if ((ppd->link_speed_supported & OPA_LINK_SPEED_12_5G) &&
-           (ppd->link_speed_enabled & OPA_LINK_SPEED_12_5G))
-               config_data |= 0x01;
-       ret = load_8051_config(ppd->dd, TX_SETTINGS, GENERAL_CONFIG,
-                              config_data);
-       if (ret != HCMD_SUCCESS)
-               dd_dev_err(
-                       ppd->dd,
-                       "%s: Failed to set external device config parameters\n",
-                       __func__);
-
-       config_data = (total_atten << 8) | (total_atten);
-
-       apply_tx_lanes(ppd, config_data, "Setting channel loss");
+       /* Set same channel loss for both TX and RX */
+       config_data = 0 | (total_atten << 16) | (total_atten << 24);
+       apply_tx_lanes(ppd, CHANNEL_LOSS_SETTINGS, config_data,
+                      "Setting channel loss");
+
+       /* Inform 8051 of cable capabilities */
+       if (ppd->qsfp_info.cache_valid) {
+               external_device_config =
+                       ((cache[QSFP_MOD_PWR_OFFS] & 0x4) << 3) |
+                       ((cache[QSFP_MOD_PWR_OFFS] & 0x8) << 2) |
+                       ((cache[QSFP_EQ_INFO_OFFS] & 0x2) << 1) |
+                       (cache[QSFP_EQ_INFO_OFFS] & 0x4);
+               ret = read_8051_config(ppd->dd, DC_HOST_COMM_SETTINGS,
+                                      GENERAL_CONFIG, &config_data);
+               /* Clear, then set the external device config field */
+               config_data &= ~(0xFF << 24);
+               config_data |= (external_device_config << 24);
+               ret = load_8051_config(ppd->dd, DC_HOST_COMM_SETTINGS,
+                                      GENERAL_CONFIG, config_data);
+               if (ret != HCMD_SUCCESS)
+                       dd_dev_info(ppd->dd,
+                                   "%s: Failed set ext device config params\n",
+                                   __func__);
+       }
 
-       if (tx_preset_index == OPA_INVALID_INDEX)
+       if (tx_preset_index == OPA_INVALID_INDEX) {
+               if (ppd->port_type == PORT_TYPE_QSFP && limiting_active)
+                       dd_dev_info(ppd->dd, "%s: Invalid Tx preset index\n",
+                                   __func__);
                return;
+       }
 
+       /* Following for limiting active channels only */
        get_platform_config_field(
                ppd->dd, PLATFORM_CONFIG_TX_PRESET_TABLE, tx_preset_index,
                TX_PRESET_TABLE_PRECUR, &tx_preset, 4);
@@ -603,7 +597,8 @@ static void apply_tunings(
 
        config_data = precur | (attn << 8) | (postcur << 16);
 
-       apply_tx_lanes(ppd, config_data, "Applying TX settings");
+       apply_tx_lanes(ppd, TX_EQ_SETTINGS, config_data,
+                      "Applying TX settings");
 }
 
 static int tune_active_qsfp(struct hfi1_pportdata *ppd, u32 *ptr_tx_preset,
@@ -766,7 +761,7 @@ void tune_serdes(struct hfi1_pportdata *ppd)
        u32 total_atten = 0;
        u32 remote_atten = 0, platform_atten = 0;
        u32 rx_preset_index, tx_preset_index;
-       u8 tuning_method = 0;
+       u8 tuning_method = 0, limiting_active = 0;
        struct hfi1_devdata *dd = ppd->dd;
 
        rx_preset_index = OPA_INVALID_INDEX;
@@ -789,7 +784,7 @@ void tune_serdes(struct hfi1_pportdata *ppd)
                                        PORT_TABLE_PORT_TYPE, &ppd->port_type,
                                        4);
        if (ret)
-               goto bail;
+               ppd->port_type = PORT_TYPE_UNKNOWN;
 
        switch (ppd->port_type) {
        case PORT_TYPE_DISCONNECTED:
@@ -853,6 +848,9 @@ void tune_serdes(struct hfi1_pportdata *ppd)
                                refresh_qsfp_cache(ppd, &ppd->qsfp_info);
                                if (ret)
                                        goto bail;
+
+                               limiting_active =
+                                               ppd->qsfp_info.limiting_active;
                        } else {
                                dd_dev_err(dd,
                                           "%s: Reading QSFP memory failed\n",
@@ -866,13 +864,18 @@ void tune_serdes(struct hfi1_pportdata *ppd)
                break;
        default:
                dd_dev_info(ppd->dd, "%s: Unknown port type\n", __func__);
-               goto bail;
+               ppd->port_type = PORT_TYPE_UNKNOWN;
+               tuning_method = OPA_UNKNOWN_TUNING;
+               total_atten = 0;
+               limiting_active = 0;
+               tx_preset_index = OPA_INVALID_INDEX;
+               break;
        }
+
        if (ppd->offline_disabled_reason ==
                        HFI1_ODR_MASK(OPA_LINKDOWN_REASON_NONE))
                apply_tunings(ppd, tx_preset_index, tuning_method,
-                             total_atten,
-                             ppd->qsfp_info.limiting_active);
+                             total_atten, limiting_active);
 
        if (!ret)
                ppd->driver_link_ready = 1;
index c9d1e64ef6816faa2056cce080f781e0949806b3..42e5be494fca824fc28393c95683c2d34a4192f0 100644 (file)
@@ -344,7 +344,6 @@ int refresh_qsfp_cache(struct hfi1_pportdata *ppd, struct qsfp_data *cp)
        ppd->qsfp_info.cache_valid = 0;
        spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags);
 
-       dd_dev_info(ppd->dd, "%s called\n", __func__);
        if (!qsfp_mod_present(ppd)) {
                ret = -ENODEV;
                goto bail;