summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorVadim Pasternak <vadimp@nvidia.com>2022-01-26 17:18:25 +0300
committerGuenter Roeck <linux@roeck-us.net>2022-02-28 04:03:17 +0300
commitda74944d3a469ffc0e8229520afbf41ad01219b6 (patch)
treefb3ed37735f8acc142c8ebc8f8ebc6c6a930c8a8 /drivers
parent4408d3600f5af8abc3258900526ae8c16b106e81 (diff)
downloadlinux-da74944d3a469ffc0e8229520afbf41ad01219b6.tar.xz
hwmon: (mlxreg-fan) Use pwm attribute for setting fan speed low limit
Recently 'cur_state' user space 'sysfs' interface 'sysfs' has been deprecated. This interface is used in Nvidia systems for setting fan speed limit. Currently fan speed limit is set from the user space by setting 'sysfs' 'cur_state' attribute to 'max_state + n', where 'n' is required limit, for example: 15 for 50% speed limit, 20 for full fan speed enforcement. The purpose of this feature is to provides ability to limit fan speed according to some system wise considerations, like absence of some replaceable units (PSU or line cards), high system ambient temperature, unreliable transceivers temperature sensing or some other factors which indirectly impacts system's airflow. The motivation is to support fan low limit feature through 'hwmon' interface. Use 'hwmon' 'pwm' attribute for setting low limit for fan speed in case 'thermal' subsystem is configured in kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal' subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm' attribute. From other side, fan speed is to be updated in hardware through 'pwm' only in case the requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan speed will be just stored with no PWM update. Signed-off-by: Vadim Pasternak <vadimp@nvidia.com> Link: https://lore.kernel.org/r/20220126141825.13545-1-vadimp@nvidia.com Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/mlxreg-fan.c84
1 files changed, 27 insertions, 57 deletions
diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c
index 4a8becdb0d58..b48bd7c961d6 100644
--- a/drivers/hwmon/mlxreg-fan.c
+++ b/drivers/hwmon/mlxreg-fan.c
@@ -18,15 +18,6 @@
#define MLXREG_FAN_MAX_STATE 10
#define MLXREG_FAN_MIN_DUTY 51 /* 20% */
#define MLXREG_FAN_MAX_DUTY 255 /* 100% */
-/*
- * Minimum and maximum FAN allowed speed in percent: from 20% to 100%. Values
- * MLXREG_FAN_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 MLXREG_FAN_SPEED_MIN (MLXREG_FAN_MAX_STATE + 2)
-#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_DIV_MIN 283
@@ -87,13 +78,16 @@ struct mlxreg_fan_tacho {
* @connected: indicates if PWM is connected;
* @reg: register offset;
* @cooling: cooling device levels;
+ * @last_hwmon_state: last cooling state set by hwmon subsystem;
+ * @last_thermal_state: last cooling state set by thermal subsystem;
* @cdev: cooling device;
*/
struct mlxreg_fan_pwm {
struct mlxreg_fan *fan;
bool connected;
u32 reg;
- u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
+ unsigned long last_hwmon_state;
+ unsigned long last_thermal_state;
struct thermal_cooling_device *cdev;
};
@@ -119,6 +113,9 @@ struct mlxreg_fan {
int divider;
};
+static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state);
+
static int
mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
@@ -213,6 +210,18 @@ mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
val > MLXREG_FAN_MAX_DUTY)
return -EINVAL;
pwm = &fan->pwm[channel];
+ /* If thermal is configured - handle PWM limit setting. */
+ if (IS_REACHABLE(CONFIG_THERMAL)) {
+ pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(val);
+ /*
+ * Update PWM only in case requested state is not less than the
+ * last thermal state.
+ */
+ if (pwm->last_hwmon_state >= pwm->last_thermal_state)
+ return mlxreg_fan_set_cur_state(pwm->cdev,
+ pwm->last_hwmon_state);
+ return 0;
+ }
return regmap_write(fan->regmap, pwm->reg, val);
default:
return -EOPNOTSUPP;
@@ -338,58 +347,22 @@ static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
{
struct mlxreg_fan_pwm *pwm = cdev->devdata;
struct mlxreg_fan *fan = pwm->fan;
- unsigned long cur_state;
- int i, config = 0;
- u32 regval;
int err;
- /*
- * 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, fan->cooling_levels[0 to 5] will be changed all
- * from 4 to 6. And state 5 (fan->cooling_levels[4]) should be
- * overwritten.
- */
- if (state >= MLXREG_FAN_SPEED_MIN && state <= MLXREG_FAN_SPEED_MAX) {
- /*
- * This is configuration change, which is only supported through sysfs.
- * For configuration non-zero value is to be returned to avoid thermal
- * statistics update.
- */
- config = 1;
- state -= MLXREG_FAN_MAX_STATE;
- for (i = 0; i < state; i++)
- pwm->cooling_levels[i] = state;
- for (i = state; i <= MLXREG_FAN_MAX_STATE; i++)
- pwm->cooling_levels[i] = i;
-
- err = regmap_read(fan->regmap, pwm->reg, &regval);
- if (err) {
- dev_err(fan->dev, "Failed to query PWM duty\n");
- return err;
- }
-
- cur_state = MLXREG_FAN_PWM_DUTY2STATE(regval);
- if (state < cur_state)
- return config;
-
- state = cur_state;
- }
-
if (state > MLXREG_FAN_MAX_STATE)
return -EINVAL;
- /* Normalize the state to the valid speed range. */
- state = pwm->cooling_levels[state];
+ /* Save thermal state. */
+ pwm->last_thermal_state = state;
+
+ state = max_t(unsigned long, state, pwm->last_hwmon_state);
err = regmap_write(fan->regmap, pwm->reg,
MLXREG_FAN_PWM_STATE2DUTY(state));
if (err) {
dev_err(fan->dev, "Failed to write PWM duty\n");
return err;
}
- return config;
+ return 0;
}
static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
@@ -564,7 +537,7 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan)
{
- int i, j;
+ int i;
for (i = 0; i < MLXREG_FAN_MAX_PWM; i++) {
struct mlxreg_fan_pwm *pwm = &fan->pwm[i];
@@ -579,11 +552,8 @@ static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan)
return PTR_ERR(pwm->cdev);
}
- /* Init cooling levels per PWM state. */
- for (j = 0; j < MLXREG_FAN_SPEED_MIN_LEVEL; j++)
- pwm->cooling_levels[j] = MLXREG_FAN_SPEED_MIN_LEVEL;
- for (j = MLXREG_FAN_SPEED_MIN_LEVEL; j <= MLXREG_FAN_MAX_STATE; j++)
- pwm->cooling_levels[j] = j;
+ /* Set minimal PWM speed. */
+ pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY);
}
return 0;