Merge tag 'nvme-5.13-2021-05-05' of git://git.infradead.org/nvme into block-5.13
[linux-2.6-microblaze.git] / drivers / hwmon / mlxreg-fan.c
index ed8d59d..116681f 100644 (file)
  * @connected: indicates if tachometer is connected;
  * @reg: register offset;
  * @mask: fault mask;
+ * @prsnt: present register offset;
  */
 struct mlxreg_fan_tacho {
        bool connected;
        u32 reg;
        u32 mask;
+       u32 prsnt;
 };
 
 /*
@@ -92,6 +94,7 @@ struct mlxreg_fan_pwm {
  * @regmap: register map of parent device;
  * @tacho: tachometer data;
  * @pwm: PWM data;
+ * @tachos_per_drwr - number of tachometers per drawer;
  * @samples: minimum allowed samples per pulse;
  * @divider: divider value for tachometer RPM calculation;
  * @cooling: cooling device levels;
@@ -103,6 +106,7 @@ struct mlxreg_fan {
        struct mlxreg_core_platform_data *pdata;
        struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
        struct mlxreg_fan_pwm pwm;
+       int tachos_per_drwr;
        int samples;
        int divider;
        u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
@@ -123,6 +127,26 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                tacho = &fan->tacho[channel];
                switch (attr) {
                case hwmon_fan_input:
+                       /*
+                        * Check FAN presence: FAN related bit in presence register is one,
+                        * if FAN is physically connected, zero - otherwise.
+                        */
+                       if (tacho->prsnt && fan->tachos_per_drwr) {
+                               err = regmap_read(fan->regmap, tacho->prsnt, &regval);
+                               if (err)
+                                       return err;
+
+                               /*
+                                * Map channel to presence bit - drawer can be equipped with
+                                * one or few FANs, while presence is indicated per drawer.
+                                */
+                               if (BIT(channel / fan->tachos_per_drwr) & regval) {
+                                       /* FAN is not connected - return zero for FAN speed. */
+                                       *val = 0;
+                                       return 0;
+                               }
+                       }
+
                        err = regmap_read(fan->regmap, tacho->reg, &regval);
                        if (err)
                                return err;
@@ -389,8 +413,8 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                             struct mlxreg_core_platform_data *pdata)
 {
        struct mlxreg_core_data *data = pdata->data;
+       int tacho_num = 0, tacho_avail = 0, i;
        bool configured = false;
-       int tacho_num = 0, i;
        int err;
 
        fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
@@ -415,7 +439,9 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
 
                        fan->tacho[tacho_num].reg = data->reg;
                        fan->tacho[tacho_num].mask = data->mask;
+                       fan->tacho[tacho_num].prsnt = data->reg_prsnt;
                        fan->tacho[tacho_num++].connected = true;
+                       tacho_avail++;
                } else if (strnstr(data->label, "pwm", sizeof(data->label))) {
                        if (fan->pwm.connected) {
                                dev_err(fan->dev, "duplicate pwm entry: %s\n",
@@ -453,6 +479,29 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                }
        }
 
+       if (pdata->capability) {
+               int drwr_avail;
+               u32 regval;
+
+               /* Obtain the number of FAN drawers, supported by system. */
+               err = regmap_read(fan->regmap, pdata->capability, &regval);
+               if (err) {
+                       dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+                               pdata->capability);
+                       return err;
+               }
+
+               drwr_avail = hweight32(regval);
+               if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) {
+                       dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n",
+                               drwr_avail, tacho_avail);
+                       return -EINVAL;
+               }
+
+               /* Set the number of tachometers per one drawer. */
+               fan->tachos_per_drwr = tacho_avail / drwr_avail;
+       }
+
        /* Init cooling levels per PWM state. */
        for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
                fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;