hwmon: (mlxreg-fan) Add support for fan capability registers
[linux-2.6-microblaze.git] / drivers / hwmon / mlxreg-fan.c
index db8c6de..44d4b1a 100644 (file)
@@ -27,7 +27,9 @@
 #define MLXREG_FAN_SPEED_MAX                   (MLXREG_FAN_MAX_STATE * 2)
 #define MLXREG_FAN_SPEED_MIN_LEVEL             2       /* 20 percent */
 #define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF 44
-#define MLXREG_FAN_TACHO_DIVIDER_DEF           1132
+#define MLXREG_FAN_TACHO_DIV_MIN               283
+#define MLXREG_FAN_TACHO_DIV_DEF               (MLXREG_FAN_TACHO_DIV_MIN * 4)
+#define MLXREG_FAN_TACHO_DIV_SCALE_MAX 64
 /*
  * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
  * The logic in a programmable device measures the time t-high by sampling the
@@ -360,15 +362,57 @@ static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
        .set_cur_state  = mlxreg_fan_set_cur_state,
 };
 
+static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan,
+                                    struct mlxreg_core_data *data)
+{
+       u32 regval;
+       int err;
+
+       err = regmap_read(fan->regmap, data->capability, &regval);
+       if (err) {
+               dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+                       data->capability);
+               return err;
+       }
+
+       return !!(regval & data->bit);
+}
+
+static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan,
+                                       struct mlxreg_core_data *data)
+{
+       u32 regval;
+       int err;
+
+       err = regmap_read(fan->regmap, data->capability, &regval);
+       if (err) {
+               dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+                       data->capability);
+               return err;
+       }
+
+       /*
+        * Set divider value according to the capability register, in case it
+        * contains valid value. Otherwise use default value. The purpose of
+        * this validation is to protect against the old hardware, in which
+        * this register can return zero.
+        */
+       if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX)
+               fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN;
+
+       return 0;
+}
+
 static int mlxreg_fan_config(struct mlxreg_fan *fan,
                             struct mlxreg_core_platform_data *pdata)
 {
        struct mlxreg_core_data *data = pdata->data;
        bool configured = false;
        int tacho_num = 0, i;
+       int err;
 
        fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
-       fan->divider = MLXREG_FAN_TACHO_DIVIDER_DEF;
+       fan->divider = MLXREG_FAN_TACHO_DIV_DEF;
        for (i = 0; i < pdata->counter; i++, data++) {
                if (strnstr(data->label, "tacho", sizeof(data->label))) {
                        if (tacho_num == MLXREG_FAN_MAX_TACHO) {
@@ -376,6 +420,17 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                                        data->label);
                                return -EINVAL;
                        }
+
+                       if (data->capability) {
+                               err = mlxreg_fan_connect_verify(fan, data);
+                               if (err < 0)
+                                       return err;
+                               else if (!err) {
+                                       tacho_num++;
+                                       continue;
+                               }
+                       }
+
                        fan->tacho[tacho_num].reg = data->reg;
                        fan->tacho[tacho_num].mask = data->mask;
                        fan->tacho[tacho_num++].connected = true;
@@ -394,13 +449,21 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                                return -EINVAL;
                        }
                        /* Validate that conf parameters are not zeros. */
-                       if (!data->mask || !data->bit) {
+                       if (!data->mask && !data->bit && !data->capability) {
                                dev_err(fan->dev, "invalid conf entry params: %s\n",
                                        data->label);
                                return -EINVAL;
                        }
-                       fan->samples = data->mask;
-                       fan->divider = data->bit;
+                       if (data->capability) {
+                               err = mlxreg_fan_speed_divider_get(fan, data);
+                               if (err)
+                                       return err;
+                       } else {
+                               if (data->mask)
+                                       fan->samples = data->mask;
+                               if (data->bit)
+                                       fan->divider = data->bit;
+                       }
                        configured = true;
                } else {
                        dev_err(fan->dev, "invalid label: %s\n", data->label);