| .. | .. |
|---|
| 27 | 27 | #define MLXREG_FAN_SPEED_MAX (MLXREG_FAN_MAX_STATE * 2) |
|---|
| 28 | 28 | #define MLXREG_FAN_SPEED_MIN_LEVEL 2 /* 20 percent */ |
|---|
| 29 | 29 | #define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF 44 |
|---|
| 30 | | -#define MLXREG_FAN_TACHO_DIVIDER_DEF 1132 |
|---|
| 30 | +#define MLXREG_FAN_TACHO_DIV_MIN 283 |
|---|
| 31 | +#define MLXREG_FAN_TACHO_DIV_DEF (MLXREG_FAN_TACHO_DIV_MIN * 4) |
|---|
| 32 | +#define MLXREG_FAN_TACHO_DIV_SCALE_MAX 64 |
|---|
| 31 | 33 | /* |
|---|
| 32 | 34 | * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high. |
|---|
| 33 | 35 | * The logic in a programmable device measures the time t-high by sampling the |
|---|
| .. | .. |
|---|
| 51 | 53 | */ |
|---|
| 52 | 54 | #define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \ |
|---|
| 53 | 55 | ((rval) + (s)) * (d))) |
|---|
| 54 | | -#define MLXREG_FAN_GET_FAULT(val, mask) (!((val) ^ (mask))) |
|---|
| 56 | +#define MLXREG_FAN_GET_FAULT(val, mask) ((val) == (mask)) |
|---|
| 55 | 57 | #define MLXREG_FAN_PWM_DUTY2STATE(duty) (DIV_ROUND_CLOSEST((duty) * \ |
|---|
| 56 | 58 | MLXREG_FAN_MAX_STATE, \ |
|---|
| 57 | 59 | MLXREG_FAN_MAX_DUTY)) |
|---|
| .. | .. |
|---|
| 227 | 229 | return 0; |
|---|
| 228 | 230 | } |
|---|
| 229 | 231 | |
|---|
| 230 | | -static const u32 mlxreg_fan_hwmon_fan_config[] = { |
|---|
| 231 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 232 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 233 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 234 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 235 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 236 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 237 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 238 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 239 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 240 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 241 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 242 | | - HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 243 | | - 0 |
|---|
| 244 | | -}; |
|---|
| 245 | | - |
|---|
| 246 | | -static const struct hwmon_channel_info mlxreg_fan_hwmon_fan = { |
|---|
| 247 | | - .type = hwmon_fan, |
|---|
| 248 | | - .config = mlxreg_fan_hwmon_fan_config, |
|---|
| 249 | | -}; |
|---|
| 250 | | - |
|---|
| 251 | | -static const u32 mlxreg_fan_hwmon_pwm_config[] = { |
|---|
| 252 | | - HWMON_PWM_INPUT, |
|---|
| 253 | | - 0 |
|---|
| 254 | | -}; |
|---|
| 255 | | - |
|---|
| 256 | | -static const struct hwmon_channel_info mlxreg_fan_hwmon_pwm = { |
|---|
| 257 | | - .type = hwmon_pwm, |
|---|
| 258 | | - .config = mlxreg_fan_hwmon_pwm_config, |
|---|
| 259 | | -}; |
|---|
| 260 | | - |
|---|
| 261 | 232 | static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = { |
|---|
| 262 | | - &mlxreg_fan_hwmon_fan, |
|---|
| 263 | | - &mlxreg_fan_hwmon_pwm, |
|---|
| 233 | + HWMON_CHANNEL_INFO(fan, |
|---|
| 234 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 235 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 236 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 237 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 238 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 239 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 240 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 241 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 242 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 243 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 244 | + HWMON_F_INPUT | HWMON_F_FAULT, |
|---|
| 245 | + HWMON_F_INPUT | HWMON_F_FAULT), |
|---|
| 246 | + HWMON_CHANNEL_INFO(pwm, |
|---|
| 247 | + HWMON_PWM_INPUT), |
|---|
| 264 | 248 | NULL |
|---|
| 265 | 249 | }; |
|---|
| 266 | 250 | |
|---|
| .. | .. |
|---|
| 366 | 350 | .set_cur_state = mlxreg_fan_set_cur_state, |
|---|
| 367 | 351 | }; |
|---|
| 368 | 352 | |
|---|
| 353 | +static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan, |
|---|
| 354 | + struct mlxreg_core_data *data) |
|---|
| 355 | +{ |
|---|
| 356 | + u32 regval; |
|---|
| 357 | + int err; |
|---|
| 358 | + |
|---|
| 359 | + err = regmap_read(fan->regmap, data->capability, ®val); |
|---|
| 360 | + if (err) { |
|---|
| 361 | + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", |
|---|
| 362 | + data->capability); |
|---|
| 363 | + return err; |
|---|
| 364 | + } |
|---|
| 365 | + |
|---|
| 366 | + return !!(regval & data->bit); |
|---|
| 367 | +} |
|---|
| 368 | + |
|---|
| 369 | +static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan, |
|---|
| 370 | + struct mlxreg_core_data *data) |
|---|
| 371 | +{ |
|---|
| 372 | + u32 regval; |
|---|
| 373 | + int err; |
|---|
| 374 | + |
|---|
| 375 | + err = regmap_read(fan->regmap, data->capability, ®val); |
|---|
| 376 | + if (err) { |
|---|
| 377 | + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", |
|---|
| 378 | + data->capability); |
|---|
| 379 | + return err; |
|---|
| 380 | + } |
|---|
| 381 | + |
|---|
| 382 | + /* |
|---|
| 383 | + * Set divider value according to the capability register, in case it |
|---|
| 384 | + * contains valid value. Otherwise use default value. The purpose of |
|---|
| 385 | + * this validation is to protect against the old hardware, in which |
|---|
| 386 | + * this register can return zero. |
|---|
| 387 | + */ |
|---|
| 388 | + if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX) |
|---|
| 389 | + fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN; |
|---|
| 390 | + |
|---|
| 391 | + return 0; |
|---|
| 392 | +} |
|---|
| 393 | + |
|---|
| 369 | 394 | static int mlxreg_fan_config(struct mlxreg_fan *fan, |
|---|
| 370 | 395 | struct mlxreg_core_platform_data *pdata) |
|---|
| 371 | 396 | { |
|---|
| 372 | 397 | struct mlxreg_core_data *data = pdata->data; |
|---|
| 373 | 398 | bool configured = false; |
|---|
| 374 | 399 | int tacho_num = 0, i; |
|---|
| 400 | + int err; |
|---|
| 375 | 401 | |
|---|
| 376 | 402 | fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF; |
|---|
| 377 | | - fan->divider = MLXREG_FAN_TACHO_DIVIDER_DEF; |
|---|
| 403 | + fan->divider = MLXREG_FAN_TACHO_DIV_DEF; |
|---|
| 378 | 404 | for (i = 0; i < pdata->counter; i++, data++) { |
|---|
| 379 | 405 | if (strnstr(data->label, "tacho", sizeof(data->label))) { |
|---|
| 380 | 406 | if (tacho_num == MLXREG_FAN_MAX_TACHO) { |
|---|
| .. | .. |
|---|
| 382 | 408 | data->label); |
|---|
| 383 | 409 | return -EINVAL; |
|---|
| 384 | 410 | } |
|---|
| 411 | + |
|---|
| 412 | + if (data->capability) { |
|---|
| 413 | + err = mlxreg_fan_connect_verify(fan, data); |
|---|
| 414 | + if (err < 0) |
|---|
| 415 | + return err; |
|---|
| 416 | + else if (!err) { |
|---|
| 417 | + tacho_num++; |
|---|
| 418 | + continue; |
|---|
| 419 | + } |
|---|
| 420 | + } |
|---|
| 421 | + |
|---|
| 385 | 422 | fan->tacho[tacho_num].reg = data->reg; |
|---|
| 386 | 423 | fan->tacho[tacho_num].mask = data->mask; |
|---|
| 387 | 424 | fan->tacho[tacho_num++].connected = true; |
|---|
| .. | .. |
|---|
| 400 | 437 | return -EINVAL; |
|---|
| 401 | 438 | } |
|---|
| 402 | 439 | /* Validate that conf parameters are not zeros. */ |
|---|
| 403 | | - if (!data->mask || !data->bit) { |
|---|
| 440 | + if (!data->mask && !data->bit && !data->capability) { |
|---|
| 404 | 441 | dev_err(fan->dev, "invalid conf entry params: %s\n", |
|---|
| 405 | 442 | data->label); |
|---|
| 406 | 443 | return -EINVAL; |
|---|
| 407 | 444 | } |
|---|
| 408 | | - fan->samples = data->mask; |
|---|
| 409 | | - fan->divider = data->bit; |
|---|
| 445 | + if (data->capability) { |
|---|
| 446 | + err = mlxreg_fan_speed_divider_get(fan, data); |
|---|
| 447 | + if (err) |
|---|
| 448 | + return err; |
|---|
| 449 | + } else { |
|---|
| 450 | + if (data->mask) |
|---|
| 451 | + fan->samples = data->mask; |
|---|
| 452 | + if (data->bit) |
|---|
| 453 | + fan->divider = data->bit; |
|---|
| 454 | + } |
|---|
| 410 | 455 | configured = true; |
|---|
| 411 | 456 | } else { |
|---|
| 412 | 457 | dev_err(fan->dev, "invalid label: %s\n", data->label); |
|---|
| .. | .. |
|---|
| 426 | 471 | static int mlxreg_fan_probe(struct platform_device *pdev) |
|---|
| 427 | 472 | { |
|---|
| 428 | 473 | struct mlxreg_core_platform_data *pdata; |
|---|
| 474 | + struct device *dev = &pdev->dev; |
|---|
| 429 | 475 | struct mlxreg_fan *fan; |
|---|
| 430 | 476 | struct device *hwm; |
|---|
| 431 | 477 | int err; |
|---|
| 432 | 478 | |
|---|
| 433 | | - pdata = dev_get_platdata(&pdev->dev); |
|---|
| 479 | + pdata = dev_get_platdata(dev); |
|---|
| 434 | 480 | if (!pdata) { |
|---|
| 435 | | - dev_err(&pdev->dev, "Failed to get platform data.\n"); |
|---|
| 481 | + dev_err(dev, "Failed to get platform data.\n"); |
|---|
| 436 | 482 | return -EINVAL; |
|---|
| 437 | 483 | } |
|---|
| 438 | 484 | |
|---|
| 439 | | - fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); |
|---|
| 485 | + fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL); |
|---|
| 440 | 486 | if (!fan) |
|---|
| 441 | 487 | return -ENOMEM; |
|---|
| 442 | 488 | |
|---|
| 443 | | - fan->dev = &pdev->dev; |
|---|
| 489 | + fan->dev = dev; |
|---|
| 444 | 490 | fan->regmap = pdata->regmap; |
|---|
| 445 | | - platform_set_drvdata(pdev, fan); |
|---|
| 446 | 491 | |
|---|
| 447 | 492 | err = mlxreg_fan_config(fan, pdata); |
|---|
| 448 | 493 | if (err) |
|---|
| 449 | 494 | return err; |
|---|
| 450 | 495 | |
|---|
| 451 | | - hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan", |
|---|
| 496 | + hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan", |
|---|
| 452 | 497 | fan, |
|---|
| 453 | 498 | &mlxreg_fan_hwmon_chip_info, |
|---|
| 454 | 499 | NULL); |
|---|
| 455 | 500 | if (IS_ERR(hwm)) { |
|---|
| 456 | | - dev_err(&pdev->dev, "Failed to register hwmon device\n"); |
|---|
| 501 | + dev_err(dev, "Failed to register hwmon device\n"); |
|---|
| 457 | 502 | return PTR_ERR(hwm); |
|---|
| 458 | 503 | } |
|---|
| 459 | 504 | |
|---|
| 460 | 505 | if (IS_REACHABLE(CONFIG_THERMAL)) { |
|---|
| 461 | | - fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan, |
|---|
| 462 | | - &mlxreg_fan_cooling_ops); |
|---|
| 506 | + fan->cdev = devm_thermal_of_cooling_device_register(dev, |
|---|
| 507 | + NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops); |
|---|
| 463 | 508 | if (IS_ERR(fan->cdev)) { |
|---|
| 464 | | - dev_err(&pdev->dev, "Failed to register cooling device\n"); |
|---|
| 509 | + dev_err(dev, "Failed to register cooling device\n"); |
|---|
| 465 | 510 | return PTR_ERR(fan->cdev); |
|---|
| 466 | 511 | } |
|---|
| 467 | 512 | } |
|---|
| 468 | | - |
|---|
| 469 | | - return 0; |
|---|
| 470 | | -} |
|---|
| 471 | | - |
|---|
| 472 | | -static int mlxreg_fan_remove(struct platform_device *pdev) |
|---|
| 473 | | -{ |
|---|
| 474 | | - struct mlxreg_fan *fan = platform_get_drvdata(pdev); |
|---|
| 475 | | - |
|---|
| 476 | | - if (IS_REACHABLE(CONFIG_THERMAL)) |
|---|
| 477 | | - thermal_cooling_device_unregister(fan->cdev); |
|---|
| 478 | 513 | |
|---|
| 479 | 514 | return 0; |
|---|
| 480 | 515 | } |
|---|
| .. | .. |
|---|
| 484 | 519 | .name = "mlxreg-fan", |
|---|
| 485 | 520 | }, |
|---|
| 486 | 521 | .probe = mlxreg_fan_probe, |
|---|
| 487 | | - .remove = mlxreg_fan_remove, |
|---|
| 488 | 522 | }; |
|---|
| 489 | 523 | |
|---|
| 490 | 524 | module_platform_driver(mlxreg_fan_driver); |
|---|