mlxsw: core: Extend hwmon interface with QSFP module temperature attributes
authorVadim Pasternak <vadimp@mellanox.com>
Wed, 13 Feb 2019 11:28:54 +0000 (11:28 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 14 Feb 2019 06:33:02 +0000 (22:33 -0800)
Add new attributes to hwmon object for exposing QSFP module temperature
input, fault indication, critical and emergency thresholds. Temperature
input and fault indication are read from Management Temperature Bulk
Register. Temperature thresholds are read from Management Cable Info
Access Register.

Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c

index 40e13093b62d3646a254c96a4b08f2e6ad84ce2e..504f6bba8874e43e67f0fce7eae982f8e7cdb5e1 100644 (file)
@@ -7,8 +7,10 @@
 #include <linux/sysfs.h>
 #include <linux/hwmon.h>
 #include <linux/err.h>
+#include <linux/sfp.h>
 
 #include "core.h"
+#include "core_env.h"
 
 #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
 #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
@@ -30,6 +32,7 @@ struct mlxsw_hwmon {
        struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
        struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
        unsigned int attrs_count;
+       u8 sensor_count;
 };
 
 static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
@@ -188,6 +191,136 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
        return len;
 }
 
+static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
+       u16 temp;
+       u8 module;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
+                           1);
+       err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err) {
+               dev_err(dev, "Failed to query module temprature sensor\n");
+               return err;
+       }
+
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
+       /* Update status and temperature cache. */
+       switch (temp) {
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA:
+               temp = 0;
+               break;
+       case MLXSW_REG_MTBR_BAD_SENS_INFO:
+               /* Untrusted cable is connected. Reading temperature from its
+                * sensor is faulty.
+                */
+               temp = 0;
+               break;
+       default:
+               temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+               break;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
+       u8 module, fault;
+       u16 temp;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
+                           1);
+       err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err) {
+               dev_err(dev, "Failed to query module temprature sensor\n");
+               return err;
+       }
+
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
+
+       /* Update status and temperature cache. */
+       switch (temp) {
+       case MLXSW_REG_MTBR_BAD_SENS_INFO:
+               /* Untrusted cable is connected. Reading temperature from its
+                * sensor is faulty.
+                */
+               fault = 1;
+               break;
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA:
+       default:
+               fault = 0;
+               break;
+       }
+
+       return sprintf(buf, "%u\n", fault);
+}
+
+static ssize_t
+mlxsw_hwmon_module_temp_critical_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       int temp;
+       u8 module;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
+                                                  SFP_TEMP_HIGH_WARN, &temp);
+       if (err) {
+               dev_err(dev, "Failed to query module temprature thresholds\n");
+               return err;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t
+mlxsw_hwmon_module_temp_emergency_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       u8 module;
+       int temp;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
+                                                  SFP_TEMP_HIGH_ALARM, &temp);
+       if (err) {
+               dev_err(dev, "Failed to query module temprature thresholds\n");
+               return err;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
 enum mlxsw_hwmon_attr_type {
        MLXSW_HWMON_ATTR_TYPE_TEMP,
        MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
@@ -195,6 +328,10 @@ enum mlxsw_hwmon_attr_type {
        MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
        MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
        MLXSW_HWMON_ATTR_TYPE_PWM,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
 };
 
 static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
@@ -244,6 +381,33 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
                snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
                         "pwm%u", num + 1);
                break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE:
+               mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_input", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT:
+               mlxsw_hwmon_attr->dev_attr.show =
+                                       mlxsw_hwmon_module_temp_fault_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_fault", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT:
+               mlxsw_hwmon_attr->dev_attr.show =
+                       mlxsw_hwmon_module_temp_critical_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_crit", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG:
+               mlxsw_hwmon_attr->dev_attr.show =
+                       mlxsw_hwmon_module_temp_emergency_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_emergency", num + 1);
+               break;
        default:
                WARN_ON(1);
        }
@@ -261,7 +425,6 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
 {
        char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
        char mtmp_pl[MLXSW_REG_MTMP_LEN];
-       u8 sensor_count;
        int i;
        int err;
 
@@ -270,8 +433,8 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
                dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
                return err;
        }
-       sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
-       for (i = 0; i < sensor_count; i++) {
+       mlxsw_hwmon->sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
+       for (i = 0; i < mlxsw_hwmon->sensor_count; i++) {
                mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
                err = mlxsw_reg_write(mlxsw_hwmon->core,
                                      MLXSW_REG(mtmp), mtmp_pl);
@@ -327,6 +490,50 @@ static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
        return 0;
 }
 
+static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+       unsigned int module_count = mlxsw_core_max_ports(mlxsw_hwmon->core);
+       char pmlp_pl[MLXSW_REG_PMLP_LEN] = {0};
+       int i, index;
+       u8 width;
+       int err;
+
+       /* Add extra attributes for module temperature. Sensor index is
+        * assigned to sensor_count value, while all indexed before
+        * sensor_count are already utilized by the sensors connected through
+        * mtmp register by mlxsw_hwmon_temp_init().
+        */
+       index = mlxsw_hwmon->sensor_count;
+       for (i = 1; i < module_count; i++) {
+               mlxsw_reg_pmlp_pack(pmlp_pl, i);
+               err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(pmlp),
+                                     pmlp_pl);
+               if (err) {
+                       dev_err(mlxsw_hwmon->bus_info->dev, "Failed to read module index %d\n",
+                               i);
+                       return err;
+               }
+               width = mlxsw_reg_pmlp_width_get(pmlp_pl);
+               if (!width)
+                       continue;
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, index,
+                                    index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
+                                    index, index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
+                                    index, index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
+                                    index, index);
+               index++;
+       }
+
+       return 0;
+}
+
 int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
                     const struct mlxsw_bus_info *mlxsw_bus_info,
                     struct mlxsw_hwmon **p_hwmon)
@@ -349,6 +556,10 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        if (err)
                goto err_fans_init;
 
+       err = mlxsw_hwmon_module_init(mlxsw_hwmon);
+       if (err)
+               goto err_temp_module_init;
+
        mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
        mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
 
@@ -365,6 +576,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        return 0;
 
 err_hwmon_register:
+err_temp_module_init:
 err_fans_init:
 err_temp_init:
        kfree(mlxsw_hwmon);