#define MLXREG_FAN_MAX_TACHO 24 #define MLXREG_FAN_MAX_PWM 4 #define MLXREG_FAN_PWM_NOT_CONNECTED 0xff #define MLXREG_FAN_MAX_STATE 10 #define MLXREG_FAN_MIN_DUTY 51 /* 20% */ #define MLXREG_FAN_MAX_DUTY 255 /* 100% */ #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 #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 * tachometer every t-sample (with the default value 11.32 uS) and increment * a counter (N) as long as the pulse has not change: * RPM = 15 / (t-sample * (K + Regval)), where: * Regval: is the value read from the programmable device register; * - 0xff - represents tachometer fault; * - 0xfe - represents tachometer minimum value , which is 4444 RPM; * - 0x00 - represents tachometer maximum value , which is 300000 RPM; * K: is 44 and it represents the minimum allowed samples per pulse; * N: is equal K + Regval; * In order to calculate RPM from the register value the following formula is * used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in the * default case is modified to: * RPM = 15000000 * 100 / ((Regval + 44) * 1132); * - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115; * - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446; * In common case the formula is modified to: * RPM = 15000000 * 100 / ((Regval + samples) * divider).
*/ #define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \
((rval) + (s)) * (d))) #define MLXREG_FAN_GET_FAULT(val, mask) ((val) == (mask)) #define MLXREG_FAN_PWM_DUTY2STATE(duty) (DIV_ROUND_CLOSEST((duty) * \
MLXREG_FAN_MAX_STATE, \
MLXREG_FAN_MAX_DUTY)) #define MLXREG_FAN_PWM_STATE2DUTY(stat) (DIV_ROUND_CLOSEST((stat) * \
MLXREG_FAN_MAX_DUTY, \
MLXREG_FAN_MAX_STATE))
staticint
mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val)
{ struct mlxreg_fan *fan = dev_get_drvdata(dev); struct mlxreg_fan_tacho *tacho; struct mlxreg_fan_pwm *pwm;
u32 regval; int err;
switch (type) { case hwmon_fan:
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, ®val); 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, ®val); if (err) return err;
if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) { /* FAN is broken - return zero for FAN speed. */
*val = 0; return 0;
}
switch (type) { case hwmon_pwm: switch (attr) { case hwmon_pwm_input: if (val < MLXREG_FAN_MIN_DUTY ||
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, false); return 0;
} return regmap_write(fan->regmap, pwm->reg, val); default: return -EOPNOTSUPP;
} break;
default: return -EOPNOTSUPP;
}
return -EOPNOTSUPP;
}
static umode_t
mlxreg_fan_is_visible(constvoid *data, enum hwmon_sensor_types type, u32 attr, int channel)
{ switch (type) { case hwmon_fan: if (!(((struct mlxreg_fan *)data)->tacho[channel].connected)) return 0;
switch (attr) { case hwmon_fan_input: case hwmon_fan_fault: return 0444; default: break;
} break;
case hwmon_pwm: if (!(((struct mlxreg_fan *)data)->pwm[channel].connected)) return 0;
err = regmap_read(fan->regmap, data->capability, ®val); 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;
/* Validate if more then one PWM is connected. */ if (pwm_num) {
err = mlxreg_pwm_connect_verify(fan, data); if (err < 0) return err; elseif (!err) continue;
}
if (pdata->capability) { int drwr_avail;
u32 regval;
/* Obtain the number of FAN drawers, supported by system. */
err = regmap_read(fan->regmap, pdata->capability, ®val); 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;
}
return 0;
}
staticint mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan)
{ int i;
for (i = 0; i < MLXREG_FAN_MAX_PWM; i++) { struct mlxreg_fan_pwm *pwm = &fan->pwm[i];
if (!pwm->connected) continue;
pwm->fan = fan; /* Set minimal PWM speed. */
pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY);
pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i],
pwm, &mlxreg_fan_cooling_ops); if (IS_ERR(pwm->cdev)) {
dev_err(dev, "Failed to register cooling device\n"); return PTR_ERR(pwm->cdev);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.