Extend cooling device with cooling levels vector to allow more
flexibility of PWM setting.
Thermal zone algorithm operates with the numerical states for PWM
setting. Each state is the index, defined in range from 0 to 10 and
it's mapped to the relevant duty cycle value, which is written to PWM
controller. With the current definition FAN speed is set to 0% for
state 0, 10% for state 1, and so on up to 100% for the maximum state
10.
Some systems have limitation for the PWM speed minimum. For such
systems PWM setting speed to 0% will just disable the ability to
increase speed anymore and such device will be stall on zero speed.
Cooling levels allow to configure state vector according to the
particular system requirements. For example, if PWM speed is not
allowed to be below 30%, cooling levels could be configured as 30%,
30%, 30%, 30%, 40%, 50% and so on.

Signed-off-by: Vadim Pasternak <vad...@mellanox.com>
Acked-by: Jiri Pirko <j...@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/core_thermal.c | 59 +++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c 
b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
index 1587820..53e4ef9 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
@@ -46,6 +46,15 @@
 #define MLXSW_THERMAL_HYSTERESIS_TEMP  5000    /* 5C */
 #define MLXSW_THERMAL_MAX_STATE                10
 #define MLXSW_THERMAL_MAX_DUTY         255
+/* Minimum and maximum FAN allowed speed in percent: from 20% to 100%. Values
+ * MLXSW_THERMAL_MAX_STATE + x, where x is between 2 and 10 are used for
+ * setting FAN speed dynamic minimum. For example, if value is set to 14 (40%)
+ * cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to
+ * introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100.
+ */
+#define MLXSW_THERMAL_SPEED_MIN                (MLXSW_THERMAL_MAX_STATE + 2)
+#define MLXSW_THERMAL_SPEED_MAX                (MLXSW_THERMAL_MAX_STATE * 2)
+#define MLXSW_THERMAL_SPEED_MIN_LEVEL  2       /* 20 percent */
 
 struct mlxsw_thermal_trip {
        int     type;
@@ -97,6 +106,7 @@ struct mlxsw_thermal {
        struct thermal_zone_device *tzdev;
        int polling_delay;
        struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
+       u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
        struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
        enum thermal_device_mode mode;
 };
@@ -361,12 +371,52 @@ static int mlxsw_thermal_set_cur_state(struct 
thermal_cooling_device *cdev,
        struct mlxsw_thermal *thermal = cdev->devdata;
        struct device *dev = thermal->bus_info->dev;
        char mfsc_pl[MLXSW_REG_MFSC_LEN];
-       int err, idx;
+       unsigned long cur_state;
+       int idx, i;
+       u8 duty;
+       int err;
 
        idx = mlxsw_get_cooling_device_idx(thermal, cdev);
        if (idx < 0)
                return idx;
 
+       /* Verify if this request is for changing allowed FAN dynamical
+        * minimum. If it is - update cooling levels accordingly and update
+        * state, if current state is below the newly requested minimum state.
+        * For example, if current state is 5, and minimal state is to be
+        * changed from 4 to 6, thermal->cooling_levels[0 to 5] will be changed
+        * all from 4 to 6. And state 5 (thermal->cooling_levels[4]) should be
+        * overwritten.
+        */
+       if (state >= MLXSW_THERMAL_SPEED_MIN &&
+           state <= MLXSW_THERMAL_SPEED_MAX) {
+               state -= MLXSW_THERMAL_MAX_STATE;
+               for (i = 0; i < state; i++)
+                       thermal->cooling_levels[i] = state;
+               for (i = state; i <= MLXSW_THERMAL_MAX_STATE; i++)
+                       thermal->cooling_levels[i] = i;
+
+               mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0);
+               err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
+               if (err) {
+                       dev_err(dev, "Failed to query PWM duty\n");
+                       return err;
+               }
+
+               duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl);
+               cur_state = mlxsw_duty_to_state(duty);
+
+               if (state < cur_state)
+                       return 0;
+
+               state = cur_state;
+       }
+
+       if (state > MLXSW_THERMAL_MAX_STATE)
+               return -EINVAL;
+
+       /* Normalize the state to the valid speed range. */
+       state = thermal->cooling_levels[state];
        mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state));
        err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
        if (err) {
@@ -445,6 +495,13 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
                }
        }
 
+       /* Init cooling levels per PWM state. */
+       for (i = 0; i < MLXSW_THERMAL_SPEED_MIN_LEVEL; i++)
+               thermal->cooling_levels[i] = MLXSW_THERMAL_SPEED_MIN_LEVEL;
+       for (i = MLXSW_THERMAL_SPEED_MIN_LEVEL;
+            i <= MLXSW_THERMAL_MAX_STATE; i++)
+               thermal->cooling_levels[i] = i;
+
        if (bus_info->low_frequency)
                thermal->polling_delay = MLXSW_THERMAL_SLOW_POLL_INT;
        else
-- 
2.1.4

Reply via email to