hwmon: (mlxreg-fan) Add support for fan drawers capability and present registers
Add support for fan drawer's capability and present registers in order to set mapping between the fan drawers and tachometers. Some systems are equipped with fan drawers with one tachometer inside. Others with fan drawers with several tachometers inside. Using present register along with tachometer-to-drawer mapping allows to skip reading missed tachometers and expose input for them as zero, instead of exposing fault code returned by hardware. Signed-off-by: Vadim Pasternak <vadimp@nvidia.com> Link: https://lore.kernel.org/r/20210322172237.2213584-1-vadimp@nvidia.com Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Родитель
cb3d37b590
Коммит
f7bf7eb2d7
|
@ -67,11 +67,13 @@
|
||||||
* @connected: indicates if tachometer is connected;
|
* @connected: indicates if tachometer is connected;
|
||||||
* @reg: register offset;
|
* @reg: register offset;
|
||||||
* @mask: fault mask;
|
* @mask: fault mask;
|
||||||
|
* @prsnt: present register offset;
|
||||||
*/
|
*/
|
||||||
struct mlxreg_fan_tacho {
|
struct mlxreg_fan_tacho {
|
||||||
bool connected;
|
bool connected;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
u32 mask;
|
u32 mask;
|
||||||
|
u32 prsnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -92,6 +94,7 @@ struct mlxreg_fan_pwm {
|
||||||
* @regmap: register map of parent device;
|
* @regmap: register map of parent device;
|
||||||
* @tacho: tachometer data;
|
* @tacho: tachometer data;
|
||||||
* @pwm: PWM data;
|
* @pwm: PWM data;
|
||||||
|
* @tachos_per_drwr - number of tachometers per drawer;
|
||||||
* @samples: minimum allowed samples per pulse;
|
* @samples: minimum allowed samples per pulse;
|
||||||
* @divider: divider value for tachometer RPM calculation;
|
* @divider: divider value for tachometer RPM calculation;
|
||||||
* @cooling: cooling device levels;
|
* @cooling: cooling device levels;
|
||||||
|
@ -103,6 +106,7 @@ struct mlxreg_fan {
|
||||||
struct mlxreg_core_platform_data *pdata;
|
struct mlxreg_core_platform_data *pdata;
|
||||||
struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
|
struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
|
||||||
struct mlxreg_fan_pwm pwm;
|
struct mlxreg_fan_pwm pwm;
|
||||||
|
int tachos_per_drwr;
|
||||||
int samples;
|
int samples;
|
||||||
int divider;
|
int divider;
|
||||||
u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
|
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];
|
tacho = &fan->tacho[channel];
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_fan_input:
|
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);
|
err = regmap_read(fan->regmap, tacho->reg, ®val);
|
||||||
if (err)
|
if (err)
|
||||||
return 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_platform_data *pdata)
|
||||||
{
|
{
|
||||||
struct mlxreg_core_data *data = pdata->data;
|
struct mlxreg_core_data *data = pdata->data;
|
||||||
|
int tacho_num = 0, tacho_avail = 0, i;
|
||||||
bool configured = false;
|
bool configured = false;
|
||||||
int tacho_num = 0, i;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
|
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].reg = data->reg;
|
||||||
fan->tacho[tacho_num].mask = data->mask;
|
fan->tacho[tacho_num].mask = data->mask;
|
||||||
|
fan->tacho[tacho_num].prsnt = data->reg_prsnt;
|
||||||
fan->tacho[tacho_num++].connected = true;
|
fan->tacho[tacho_num++].connected = true;
|
||||||
|
tacho_avail++;
|
||||||
} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
|
} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
|
||||||
if (fan->pwm.connected) {
|
if (fan->pwm.connected) {
|
||||||
dev_err(fan->dev, "duplicate pwm entry: %s\n",
|
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, ®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;
|
||||||
|
}
|
||||||
|
|
||||||
/* Init cooling levels per PWM state. */
|
/* Init cooling levels per PWM state. */
|
||||||
for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
|
for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
|
||||||
fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
|
fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче