.. | .. |
---|
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)) |
---|
.. | .. |
---|
124 | 126 | err = regmap_read(fan->regmap, tacho->reg, ®val); |
---|
125 | 127 | if (err) |
---|
126 | 128 | return err; |
---|
| 129 | + |
---|
| 130 | + if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) { |
---|
| 131 | + /* FAN is broken - return zero for FAN speed. */ |
---|
| 132 | + *val = 0; |
---|
| 133 | + return 0; |
---|
| 134 | + } |
---|
127 | 135 | |
---|
128 | 136 | *val = MLXREG_FAN_GET_RPM(regval, fan->divider, |
---|
129 | 137 | fan->samples); |
---|
.. | .. |
---|
227 | 235 | return 0; |
---|
228 | 236 | } |
---|
229 | 237 | |
---|
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 | 238 | static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = { |
---|
262 | | - &mlxreg_fan_hwmon_fan, |
---|
263 | | - &mlxreg_fan_hwmon_pwm, |
---|
| 239 | + HWMON_CHANNEL_INFO(fan, |
---|
| 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_F_INPUT | HWMON_F_FAULT, |
---|
| 247 | + HWMON_F_INPUT | HWMON_F_FAULT, |
---|
| 248 | + HWMON_F_INPUT | HWMON_F_FAULT, |
---|
| 249 | + HWMON_F_INPUT | HWMON_F_FAULT, |
---|
| 250 | + HWMON_F_INPUT | HWMON_F_FAULT, |
---|
| 251 | + HWMON_F_INPUT | HWMON_F_FAULT), |
---|
| 252 | + HWMON_CHANNEL_INFO(pwm, |
---|
| 253 | + HWMON_PWM_INPUT), |
---|
264 | 254 | NULL |
---|
265 | 255 | }; |
---|
266 | 256 | |
---|
.. | .. |
---|
366 | 356 | .set_cur_state = mlxreg_fan_set_cur_state, |
---|
367 | 357 | }; |
---|
368 | 358 | |
---|
| 359 | +static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan, |
---|
| 360 | + struct mlxreg_core_data *data) |
---|
| 361 | +{ |
---|
| 362 | + u32 regval; |
---|
| 363 | + int err; |
---|
| 364 | + |
---|
| 365 | + err = regmap_read(fan->regmap, data->capability, ®val); |
---|
| 366 | + if (err) { |
---|
| 367 | + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", |
---|
| 368 | + data->capability); |
---|
| 369 | + return err; |
---|
| 370 | + } |
---|
| 371 | + |
---|
| 372 | + return !!(regval & data->bit); |
---|
| 373 | +} |
---|
| 374 | + |
---|
| 375 | +static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan, |
---|
| 376 | + struct mlxreg_core_data *data) |
---|
| 377 | +{ |
---|
| 378 | + u32 regval; |
---|
| 379 | + int err; |
---|
| 380 | + |
---|
| 381 | + err = regmap_read(fan->regmap, data->capability, ®val); |
---|
| 382 | + if (err) { |
---|
| 383 | + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", |
---|
| 384 | + data->capability); |
---|
| 385 | + return err; |
---|
| 386 | + } |
---|
| 387 | + |
---|
| 388 | + /* |
---|
| 389 | + * Set divider value according to the capability register, in case it |
---|
| 390 | + * contains valid value. Otherwise use default value. The purpose of |
---|
| 391 | + * this validation is to protect against the old hardware, in which |
---|
| 392 | + * this register can return zero. |
---|
| 393 | + */ |
---|
| 394 | + if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX) |
---|
| 395 | + fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN; |
---|
| 396 | + |
---|
| 397 | + return 0; |
---|
| 398 | +} |
---|
| 399 | + |
---|
369 | 400 | static int mlxreg_fan_config(struct mlxreg_fan *fan, |
---|
370 | 401 | struct mlxreg_core_platform_data *pdata) |
---|
371 | 402 | { |
---|
372 | 403 | struct mlxreg_core_data *data = pdata->data; |
---|
373 | 404 | bool configured = false; |
---|
374 | 405 | int tacho_num = 0, i; |
---|
| 406 | + int err; |
---|
375 | 407 | |
---|
376 | 408 | fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF; |
---|
377 | | - fan->divider = MLXREG_FAN_TACHO_DIVIDER_DEF; |
---|
| 409 | + fan->divider = MLXREG_FAN_TACHO_DIV_DEF; |
---|
378 | 410 | for (i = 0; i < pdata->counter; i++, data++) { |
---|
379 | 411 | if (strnstr(data->label, "tacho", sizeof(data->label))) { |
---|
380 | 412 | if (tacho_num == MLXREG_FAN_MAX_TACHO) { |
---|
.. | .. |
---|
382 | 414 | data->label); |
---|
383 | 415 | return -EINVAL; |
---|
384 | 416 | } |
---|
| 417 | + |
---|
| 418 | + if (data->capability) { |
---|
| 419 | + err = mlxreg_fan_connect_verify(fan, data); |
---|
| 420 | + if (err < 0) |
---|
| 421 | + return err; |
---|
| 422 | + else if (!err) { |
---|
| 423 | + tacho_num++; |
---|
| 424 | + continue; |
---|
| 425 | + } |
---|
| 426 | + } |
---|
| 427 | + |
---|
385 | 428 | fan->tacho[tacho_num].reg = data->reg; |
---|
386 | 429 | fan->tacho[tacho_num].mask = data->mask; |
---|
387 | 430 | fan->tacho[tacho_num++].connected = true; |
---|
.. | .. |
---|
400 | 443 | return -EINVAL; |
---|
401 | 444 | } |
---|
402 | 445 | /* Validate that conf parameters are not zeros. */ |
---|
403 | | - if (!data->mask || !data->bit) { |
---|
| 446 | + if (!data->mask && !data->bit && !data->capability) { |
---|
404 | 447 | dev_err(fan->dev, "invalid conf entry params: %s\n", |
---|
405 | 448 | data->label); |
---|
406 | 449 | return -EINVAL; |
---|
407 | 450 | } |
---|
408 | | - fan->samples = data->mask; |
---|
409 | | - fan->divider = data->bit; |
---|
| 451 | + if (data->capability) { |
---|
| 452 | + err = mlxreg_fan_speed_divider_get(fan, data); |
---|
| 453 | + if (err) |
---|
| 454 | + return err; |
---|
| 455 | + } else { |
---|
| 456 | + if (data->mask) |
---|
| 457 | + fan->samples = data->mask; |
---|
| 458 | + if (data->bit) |
---|
| 459 | + fan->divider = data->bit; |
---|
| 460 | + } |
---|
410 | 461 | configured = true; |
---|
411 | 462 | } else { |
---|
412 | 463 | dev_err(fan->dev, "invalid label: %s\n", data->label); |
---|
.. | .. |
---|
426 | 477 | static int mlxreg_fan_probe(struct platform_device *pdev) |
---|
427 | 478 | { |
---|
428 | 479 | struct mlxreg_core_platform_data *pdata; |
---|
| 480 | + struct device *dev = &pdev->dev; |
---|
429 | 481 | struct mlxreg_fan *fan; |
---|
430 | 482 | struct device *hwm; |
---|
431 | 483 | int err; |
---|
432 | 484 | |
---|
433 | | - pdata = dev_get_platdata(&pdev->dev); |
---|
| 485 | + pdata = dev_get_platdata(dev); |
---|
434 | 486 | if (!pdata) { |
---|
435 | | - dev_err(&pdev->dev, "Failed to get platform data.\n"); |
---|
| 487 | + dev_err(dev, "Failed to get platform data.\n"); |
---|
436 | 488 | return -EINVAL; |
---|
437 | 489 | } |
---|
438 | 490 | |
---|
439 | | - fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); |
---|
| 491 | + fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL); |
---|
440 | 492 | if (!fan) |
---|
441 | 493 | return -ENOMEM; |
---|
442 | 494 | |
---|
443 | | - fan->dev = &pdev->dev; |
---|
| 495 | + fan->dev = dev; |
---|
444 | 496 | fan->regmap = pdata->regmap; |
---|
445 | | - platform_set_drvdata(pdev, fan); |
---|
446 | 497 | |
---|
447 | 498 | err = mlxreg_fan_config(fan, pdata); |
---|
448 | 499 | if (err) |
---|
449 | 500 | return err; |
---|
450 | 501 | |
---|
451 | | - hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan", |
---|
| 502 | + hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan", |
---|
452 | 503 | fan, |
---|
453 | 504 | &mlxreg_fan_hwmon_chip_info, |
---|
454 | 505 | NULL); |
---|
455 | 506 | if (IS_ERR(hwm)) { |
---|
456 | | - dev_err(&pdev->dev, "Failed to register hwmon device\n"); |
---|
| 507 | + dev_err(dev, "Failed to register hwmon device\n"); |
---|
457 | 508 | return PTR_ERR(hwm); |
---|
458 | 509 | } |
---|
459 | 510 | |
---|
460 | 511 | if (IS_REACHABLE(CONFIG_THERMAL)) { |
---|
461 | | - fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan, |
---|
462 | | - &mlxreg_fan_cooling_ops); |
---|
| 512 | + fan->cdev = devm_thermal_of_cooling_device_register(dev, |
---|
| 513 | + NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops); |
---|
463 | 514 | if (IS_ERR(fan->cdev)) { |
---|
464 | | - dev_err(&pdev->dev, "Failed to register cooling device\n"); |
---|
| 515 | + dev_err(dev, "Failed to register cooling device\n"); |
---|
465 | 516 | return PTR_ERR(fan->cdev); |
---|
466 | 517 | } |
---|
467 | 518 | } |
---|
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 | 519 | |
---|
479 | 520 | return 0; |
---|
480 | 521 | } |
---|
.. | .. |
---|
484 | 525 | .name = "mlxreg-fan", |
---|
485 | 526 | }, |
---|
486 | 527 | .probe = mlxreg_fan_probe, |
---|
487 | | - .remove = mlxreg_fan_remove, |
---|
488 | 528 | }; |
---|
489 | 529 | |
---|
490 | 530 | module_platform_driver(mlxreg_fan_driver); |
---|