Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: sfp: Add HWMON support for module sensors

SFP modules can contain a number of sensors. The EEPROM also contains
recommended alarm and critical values for each sensor, and indications
of if these have been exceeded. Export this information via
HWMON. Currently temperature, VCC, bias current, transmit power, and
possibly receiver power is supported.

The sensors in the modules can either return calibrate or uncalibrated
values. Uncalibrated values need to be manipulated, using coefficients
provided in the SFP EEPROM. Uncalibrated receive power values require
floating point maths in order to calibrate them. Performing this in
the kernel is hard. So if the SFP module indicates it uses
uncalibrated values, RX power is not made available.

With this hwmon device, it is possible to view the sensor values using
lm-sensors programs:

in0: +3.29 V (crit min = +2.90 V, min = +3.00 V)
(max = +3.60 V, crit max = +3.70 V)
temp1: +33.0°C (low = -5.0°C, high = +80.0°C)
(crit low = -10.0°C, crit = +85.0°C)
power1: 1000.00 nW (max = 794.00 uW, min = 50.00 uW) ALARM (LCRIT)
(lcrit = 40.00 uW, crit = 1000.00 uW)
curr1: +0.00 A (crit min = +0.00 A, min = +0.00 A) ALARM (LCRIT, MIN)
(max = +0.01 A, crit max = +0.01 A)

The scaling sensors performs on the bias current is not particularly
good. The raw values are more useful:

curr1:
curr1_input: 0.000
curr1_min: 0.002
curr1_max: 0.010
curr1_lcrit: 0.000
curr1_crit: 0.011
curr1_min_alarm: 1.000
curr1_max_alarm: 0.000
curr1_lcrit_alarm: 1.000
curr1_crit_alarm: 0.000

In order to keep the I2C overhead to a minimum, the constant values,
such as limits and calibration coefficients are read once at module
insertion time. Thus only reading *_input and *_alarm properties
requires i2c read operations.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Andrew Lunn and committed by
David S. Miller
1323061a dcb5d0fc

+799 -1
+1
drivers/net/phy/Kconfig
··· 215 215 tristate "SFP cage support" 216 216 depends on I2C && PHYLINK 217 217 select MDIO_I2C 218 + imply HWMON 218 219 219 220 config AMD_PHY 220 221 tristate "AMD PHYs"
+727
drivers/net/phy/sfp.c
··· 1 + #include <linux/ctype.h> 1 2 #include <linux/delay.h> 2 3 #include <linux/gpio/consumer.h> 4 + #include <linux/hwmon.h> 3 5 #include <linux/i2c.h> 4 6 #include <linux/interrupt.h> 5 7 #include <linux/jiffies.h> ··· 133 131 unsigned int sm_retries; 134 132 135 133 struct sfp_eeprom_id id; 134 + #if IS_ENABLED(CONFIG_HWMON) 135 + struct sfp_diag diag; 136 + struct device *hwmon_dev; 137 + char *hwmon_name; 138 + #endif 139 + 136 140 }; 137 141 138 142 static bool sff_module_supported(const struct sfp_eeprom_id *id) ··· 323 315 324 316 return check; 325 317 } 318 + 319 + /* hwmon */ 320 + #if IS_ENABLED(CONFIG_HWMON) 321 + static umode_t sfp_hwmon_is_visible(const void *data, 322 + enum hwmon_sensor_types type, 323 + u32 attr, int channel) 324 + { 325 + const struct sfp *sfp = data; 326 + 327 + switch (type) { 328 + case hwmon_temp: 329 + switch (attr) { 330 + case hwmon_temp_input: 331 + case hwmon_temp_min_alarm: 332 + case hwmon_temp_max_alarm: 333 + case hwmon_temp_lcrit_alarm: 334 + case hwmon_temp_crit_alarm: 335 + case hwmon_temp_min: 336 + case hwmon_temp_max: 337 + case hwmon_temp_lcrit: 338 + case hwmon_temp_crit: 339 + return 0444; 340 + default: 341 + return 0; 342 + } 343 + case hwmon_in: 344 + switch (attr) { 345 + case hwmon_in_input: 346 + case hwmon_in_min_alarm: 347 + case hwmon_in_max_alarm: 348 + case hwmon_in_lcrit_alarm: 349 + case hwmon_in_crit_alarm: 350 + case hwmon_in_min: 351 + case hwmon_in_max: 352 + case hwmon_in_lcrit: 353 + case hwmon_in_crit: 354 + return 0444; 355 + default: 356 + return 0; 357 + } 358 + case hwmon_curr: 359 + switch (attr) { 360 + case hwmon_curr_input: 361 + case hwmon_curr_min_alarm: 362 + case hwmon_curr_max_alarm: 363 + case hwmon_curr_lcrit_alarm: 364 + case hwmon_curr_crit_alarm: 365 + case hwmon_curr_min: 366 + case hwmon_curr_max: 367 + case hwmon_curr_lcrit: 368 + case hwmon_curr_crit: 369 + return 0444; 370 + default: 371 + return 0; 372 + } 373 + case hwmon_power: 374 + /* External calibration of receive power requires 375 + * floating point arithmetic. Doing that in the kernel 376 + * is not easy, so just skip it. If the module does 377 + * not require external calibration, we can however 378 + * show receiver power, since FP is then not needed. 379 + */ 380 + if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL && 381 + channel == 1) 382 + return 0; 383 + switch (attr) { 384 + case hwmon_power_input: 385 + case hwmon_power_min_alarm: 386 + case hwmon_power_max_alarm: 387 + case hwmon_power_lcrit_alarm: 388 + case hwmon_power_crit_alarm: 389 + case hwmon_power_min: 390 + case hwmon_power_max: 391 + case hwmon_power_lcrit: 392 + case hwmon_power_crit: 393 + return 0444; 394 + default: 395 + return 0; 396 + } 397 + default: 398 + return 0; 399 + } 400 + } 401 + 402 + static int sfp_hwmon_read_sensor(struct sfp *sfp, int reg, long *value) 403 + { 404 + __be16 val; 405 + int err; 406 + 407 + err = sfp_read(sfp, true, reg, &val, sizeof(val)); 408 + if (err < 0) 409 + return err; 410 + 411 + *value = be16_to_cpu(val); 412 + 413 + return 0; 414 + } 415 + 416 + static void sfp_hwmon_to_rx_power(long *value) 417 + { 418 + *value = DIV_ROUND_CLOSEST(*value, 100); 419 + } 420 + 421 + static void sfp_hwmon_calibrate(struct sfp *sfp, unsigned int slope, int offset, 422 + long *value) 423 + { 424 + if (sfp->id.ext.diagmon & SFP_DIAGMON_EXT_CAL) 425 + *value = DIV_ROUND_CLOSEST(*value * slope, 256) + offset; 426 + } 427 + 428 + static void sfp_hwmon_calibrate_temp(struct sfp *sfp, long *value) 429 + { 430 + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_t_slope), 431 + be16_to_cpu(sfp->diag.cal_t_offset), value); 432 + 433 + if (*value >= 0x8000) 434 + *value -= 0x10000; 435 + 436 + *value = DIV_ROUND_CLOSEST(*value * 1000, 256); 437 + } 438 + 439 + static void sfp_hwmon_calibrate_vcc(struct sfp *sfp, long *value) 440 + { 441 + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_v_slope), 442 + be16_to_cpu(sfp->diag.cal_v_offset), value); 443 + 444 + *value = DIV_ROUND_CLOSEST(*value, 10); 445 + } 446 + 447 + static void sfp_hwmon_calibrate_bias(struct sfp *sfp, long *value) 448 + { 449 + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txi_slope), 450 + be16_to_cpu(sfp->diag.cal_txi_offset), value); 451 + 452 + *value = DIV_ROUND_CLOSEST(*value, 500); 453 + } 454 + 455 + static void sfp_hwmon_calibrate_tx_power(struct sfp *sfp, long *value) 456 + { 457 + sfp_hwmon_calibrate(sfp, be16_to_cpu(sfp->diag.cal_txpwr_slope), 458 + be16_to_cpu(sfp->diag.cal_txpwr_offset), value); 459 + 460 + *value = DIV_ROUND_CLOSEST(*value, 10); 461 + } 462 + 463 + static int sfp_hwmon_read_temp(struct sfp *sfp, int reg, long *value) 464 + { 465 + int err; 466 + 467 + err = sfp_hwmon_read_sensor(sfp, reg, value); 468 + if (err < 0) 469 + return err; 470 + 471 + sfp_hwmon_calibrate_temp(sfp, value); 472 + 473 + return 0; 474 + } 475 + 476 + static int sfp_hwmon_read_vcc(struct sfp *sfp, int reg, long *value) 477 + { 478 + int err; 479 + 480 + err = sfp_hwmon_read_sensor(sfp, reg, value); 481 + if (err < 0) 482 + return err; 483 + 484 + sfp_hwmon_calibrate_vcc(sfp, value); 485 + 486 + return 0; 487 + } 488 + 489 + static int sfp_hwmon_read_bias(struct sfp *sfp, int reg, long *value) 490 + { 491 + int err; 492 + 493 + err = sfp_hwmon_read_sensor(sfp, reg, value); 494 + if (err < 0) 495 + return err; 496 + 497 + sfp_hwmon_calibrate_bias(sfp, value); 498 + 499 + return 0; 500 + } 501 + 502 + static int sfp_hwmon_read_tx_power(struct sfp *sfp, int reg, long *value) 503 + { 504 + int err; 505 + 506 + err = sfp_hwmon_read_sensor(sfp, reg, value); 507 + if (err < 0) 508 + return err; 509 + 510 + sfp_hwmon_calibrate_tx_power(sfp, value); 511 + 512 + return 0; 513 + } 514 + 515 + static int sfp_hwmon_read_rx_power(struct sfp *sfp, int reg, long *value) 516 + { 517 + int err; 518 + 519 + err = sfp_hwmon_read_sensor(sfp, reg, value); 520 + if (err < 0) 521 + return err; 522 + 523 + sfp_hwmon_to_rx_power(value); 524 + 525 + return 0; 526 + } 527 + 528 + static int sfp_hwmon_temp(struct sfp *sfp, u32 attr, long *value) 529 + { 530 + u8 status; 531 + int err; 532 + 533 + switch (attr) { 534 + case hwmon_temp_input: 535 + return sfp_hwmon_read_temp(sfp, SFP_TEMP, value); 536 + 537 + case hwmon_temp_lcrit: 538 + *value = be16_to_cpu(sfp->diag.temp_low_alarm); 539 + sfp_hwmon_calibrate_temp(sfp, value); 540 + return 0; 541 + 542 + case hwmon_temp_min: 543 + *value = be16_to_cpu(sfp->diag.temp_low_warn); 544 + sfp_hwmon_calibrate_temp(sfp, value); 545 + return 0; 546 + case hwmon_temp_max: 547 + *value = be16_to_cpu(sfp->diag.temp_high_warn); 548 + sfp_hwmon_calibrate_temp(sfp, value); 549 + return 0; 550 + 551 + case hwmon_temp_crit: 552 + *value = be16_to_cpu(sfp->diag.temp_high_alarm); 553 + sfp_hwmon_calibrate_temp(sfp, value); 554 + return 0; 555 + 556 + case hwmon_temp_lcrit_alarm: 557 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 558 + if (err < 0) 559 + return err; 560 + 561 + *value = !!(status & SFP_ALARM0_TEMP_LOW); 562 + return 0; 563 + 564 + case hwmon_temp_min_alarm: 565 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 566 + if (err < 0) 567 + return err; 568 + 569 + *value = !!(status & SFP_WARN0_TEMP_LOW); 570 + return 0; 571 + 572 + case hwmon_temp_max_alarm: 573 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 574 + if (err < 0) 575 + return err; 576 + 577 + *value = !!(status & SFP_WARN0_TEMP_HIGH); 578 + return 0; 579 + 580 + case hwmon_temp_crit_alarm: 581 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 582 + if (err < 0) 583 + return err; 584 + 585 + *value = !!(status & SFP_ALARM0_TEMP_HIGH); 586 + return 0; 587 + default: 588 + return -EOPNOTSUPP; 589 + } 590 + 591 + return -EOPNOTSUPP; 592 + } 593 + 594 + static int sfp_hwmon_vcc(struct sfp *sfp, u32 attr, long *value) 595 + { 596 + u8 status; 597 + int err; 598 + 599 + switch (attr) { 600 + case hwmon_in_input: 601 + return sfp_hwmon_read_vcc(sfp, SFP_VCC, value); 602 + 603 + case hwmon_in_lcrit: 604 + *value = be16_to_cpu(sfp->diag.volt_low_alarm); 605 + sfp_hwmon_calibrate_vcc(sfp, value); 606 + return 0; 607 + 608 + case hwmon_in_min: 609 + *value = be16_to_cpu(sfp->diag.volt_low_warn); 610 + sfp_hwmon_calibrate_vcc(sfp, value); 611 + return 0; 612 + 613 + case hwmon_in_max: 614 + *value = be16_to_cpu(sfp->diag.volt_high_warn); 615 + sfp_hwmon_calibrate_vcc(sfp, value); 616 + return 0; 617 + 618 + case hwmon_in_crit: 619 + *value = be16_to_cpu(sfp->diag.volt_high_alarm); 620 + sfp_hwmon_calibrate_vcc(sfp, value); 621 + return 0; 622 + 623 + case hwmon_in_lcrit_alarm: 624 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 625 + if (err < 0) 626 + return err; 627 + 628 + *value = !!(status & SFP_ALARM0_VCC_LOW); 629 + return 0; 630 + 631 + case hwmon_in_min_alarm: 632 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 633 + if (err < 0) 634 + return err; 635 + 636 + *value = !!(status & SFP_WARN0_VCC_LOW); 637 + return 0; 638 + 639 + case hwmon_in_max_alarm: 640 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 641 + if (err < 0) 642 + return err; 643 + 644 + *value = !!(status & SFP_WARN0_VCC_HIGH); 645 + return 0; 646 + 647 + case hwmon_in_crit_alarm: 648 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 649 + if (err < 0) 650 + return err; 651 + 652 + *value = !!(status & SFP_ALARM0_VCC_HIGH); 653 + return 0; 654 + default: 655 + return -EOPNOTSUPP; 656 + } 657 + 658 + return -EOPNOTSUPP; 659 + } 660 + 661 + static int sfp_hwmon_bias(struct sfp *sfp, u32 attr, long *value) 662 + { 663 + u8 status; 664 + int err; 665 + 666 + switch (attr) { 667 + case hwmon_curr_input: 668 + return sfp_hwmon_read_bias(sfp, SFP_TX_BIAS, value); 669 + 670 + case hwmon_curr_lcrit: 671 + *value = be16_to_cpu(sfp->diag.bias_low_alarm); 672 + sfp_hwmon_calibrate_bias(sfp, value); 673 + return 0; 674 + 675 + case hwmon_curr_min: 676 + *value = be16_to_cpu(sfp->diag.bias_low_warn); 677 + sfp_hwmon_calibrate_bias(sfp, value); 678 + return 0; 679 + 680 + case hwmon_curr_max: 681 + *value = be16_to_cpu(sfp->diag.bias_high_warn); 682 + sfp_hwmon_calibrate_bias(sfp, value); 683 + return 0; 684 + 685 + case hwmon_curr_crit: 686 + *value = be16_to_cpu(sfp->diag.bias_high_alarm); 687 + sfp_hwmon_calibrate_bias(sfp, value); 688 + return 0; 689 + 690 + case hwmon_curr_lcrit_alarm: 691 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 692 + if (err < 0) 693 + return err; 694 + 695 + *value = !!(status & SFP_ALARM0_TX_BIAS_LOW); 696 + return 0; 697 + 698 + case hwmon_curr_min_alarm: 699 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 700 + if (err < 0) 701 + return err; 702 + 703 + *value = !!(status & SFP_WARN0_TX_BIAS_LOW); 704 + return 0; 705 + 706 + case hwmon_curr_max_alarm: 707 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 708 + if (err < 0) 709 + return err; 710 + 711 + *value = !!(status & SFP_WARN0_TX_BIAS_HIGH); 712 + return 0; 713 + 714 + case hwmon_curr_crit_alarm: 715 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 716 + if (err < 0) 717 + return err; 718 + 719 + *value = !!(status & SFP_ALARM0_TX_BIAS_HIGH); 720 + return 0; 721 + default: 722 + return -EOPNOTSUPP; 723 + } 724 + 725 + return -EOPNOTSUPP; 726 + } 727 + 728 + static int sfp_hwmon_tx_power(struct sfp *sfp, u32 attr, long *value) 729 + { 730 + u8 status; 731 + int err; 732 + 733 + switch (attr) { 734 + case hwmon_power_input: 735 + return sfp_hwmon_read_tx_power(sfp, SFP_TX_POWER, value); 736 + 737 + case hwmon_power_lcrit: 738 + *value = be16_to_cpu(sfp->diag.txpwr_low_alarm); 739 + sfp_hwmon_calibrate_tx_power(sfp, value); 740 + return 0; 741 + 742 + case hwmon_power_min: 743 + *value = be16_to_cpu(sfp->diag.txpwr_low_warn); 744 + sfp_hwmon_calibrate_tx_power(sfp, value); 745 + return 0; 746 + 747 + case hwmon_power_max: 748 + *value = be16_to_cpu(sfp->diag.txpwr_high_warn); 749 + sfp_hwmon_calibrate_tx_power(sfp, value); 750 + return 0; 751 + 752 + case hwmon_power_crit: 753 + *value = be16_to_cpu(sfp->diag.txpwr_high_alarm); 754 + sfp_hwmon_calibrate_tx_power(sfp, value); 755 + return 0; 756 + 757 + case hwmon_power_lcrit_alarm: 758 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 759 + if (err < 0) 760 + return err; 761 + 762 + *value = !!(status & SFP_ALARM0_TXPWR_LOW); 763 + return 0; 764 + 765 + case hwmon_power_min_alarm: 766 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 767 + if (err < 0) 768 + return err; 769 + 770 + *value = !!(status & SFP_WARN0_TXPWR_LOW); 771 + return 0; 772 + 773 + case hwmon_power_max_alarm: 774 + err = sfp_read(sfp, true, SFP_WARN0, &status, sizeof(status)); 775 + if (err < 0) 776 + return err; 777 + 778 + *value = !!(status & SFP_WARN0_TXPWR_HIGH); 779 + return 0; 780 + 781 + case hwmon_power_crit_alarm: 782 + err = sfp_read(sfp, true, SFP_ALARM0, &status, sizeof(status)); 783 + if (err < 0) 784 + return err; 785 + 786 + *value = !!(status & SFP_ALARM0_TXPWR_HIGH); 787 + return 0; 788 + default: 789 + return -EOPNOTSUPP; 790 + } 791 + 792 + return -EOPNOTSUPP; 793 + } 794 + 795 + static int sfp_hwmon_rx_power(struct sfp *sfp, u32 attr, long *value) 796 + { 797 + u8 status; 798 + int err; 799 + 800 + switch (attr) { 801 + case hwmon_power_input: 802 + return sfp_hwmon_read_rx_power(sfp, SFP_RX_POWER, value); 803 + 804 + case hwmon_power_lcrit: 805 + *value = be16_to_cpu(sfp->diag.rxpwr_low_alarm); 806 + sfp_hwmon_to_rx_power(value); 807 + return 0; 808 + 809 + case hwmon_power_min: 810 + *value = be16_to_cpu(sfp->diag.rxpwr_low_warn); 811 + sfp_hwmon_to_rx_power(value); 812 + return 0; 813 + 814 + case hwmon_power_max: 815 + *value = be16_to_cpu(sfp->diag.rxpwr_high_warn); 816 + sfp_hwmon_to_rx_power(value); 817 + return 0; 818 + 819 + case hwmon_power_crit: 820 + *value = be16_to_cpu(sfp->diag.rxpwr_high_alarm); 821 + sfp_hwmon_to_rx_power(value); 822 + return 0; 823 + 824 + case hwmon_power_lcrit_alarm: 825 + err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status)); 826 + if (err < 0) 827 + return err; 828 + 829 + *value = !!(status & SFP_ALARM1_RXPWR_LOW); 830 + return 0; 831 + 832 + case hwmon_power_min_alarm: 833 + err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status)); 834 + if (err < 0) 835 + return err; 836 + 837 + *value = !!(status & SFP_WARN1_RXPWR_LOW); 838 + return 0; 839 + 840 + case hwmon_power_max_alarm: 841 + err = sfp_read(sfp, true, SFP_WARN1, &status, sizeof(status)); 842 + if (err < 0) 843 + return err; 844 + 845 + *value = !!(status & SFP_WARN1_RXPWR_HIGH); 846 + return 0; 847 + 848 + case hwmon_power_crit_alarm: 849 + err = sfp_read(sfp, true, SFP_ALARM1, &status, sizeof(status)); 850 + if (err < 0) 851 + return err; 852 + 853 + *value = !!(status & SFP_ALARM1_RXPWR_HIGH); 854 + return 0; 855 + default: 856 + return -EOPNOTSUPP; 857 + } 858 + 859 + return -EOPNOTSUPP; 860 + } 861 + 862 + static int sfp_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 863 + u32 attr, int channel, long *value) 864 + { 865 + struct sfp *sfp = dev_get_drvdata(dev); 866 + 867 + switch (type) { 868 + case hwmon_temp: 869 + return sfp_hwmon_temp(sfp, attr, value); 870 + case hwmon_in: 871 + return sfp_hwmon_vcc(sfp, attr, value); 872 + case hwmon_curr: 873 + return sfp_hwmon_bias(sfp, attr, value); 874 + case hwmon_power: 875 + switch (channel) { 876 + case 0: 877 + return sfp_hwmon_tx_power(sfp, attr, value); 878 + case 1: 879 + return sfp_hwmon_rx_power(sfp, attr, value); 880 + default: 881 + return -EOPNOTSUPP; 882 + } 883 + default: 884 + return -EOPNOTSUPP; 885 + } 886 + } 887 + 888 + static const struct hwmon_ops sfp_hwmon_ops = { 889 + .is_visible = sfp_hwmon_is_visible, 890 + .read = sfp_hwmon_read, 891 + }; 892 + 893 + static u32 sfp_hwmon_chip_config[] = { 894 + HWMON_C_REGISTER_TZ, 895 + 0, 896 + }; 897 + 898 + static const struct hwmon_channel_info sfp_hwmon_chip = { 899 + .type = hwmon_chip, 900 + .config = sfp_hwmon_chip_config, 901 + }; 902 + 903 + static u32 sfp_hwmon_temp_config[] = { 904 + HWMON_T_INPUT | 905 + HWMON_T_MAX | HWMON_T_MIN | 906 + HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | 907 + HWMON_T_CRIT | HWMON_T_LCRIT | 908 + HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, 909 + 0, 910 + }; 911 + 912 + static const struct hwmon_channel_info sfp_hwmon_temp_channel_info = { 913 + .type = hwmon_temp, 914 + .config = sfp_hwmon_temp_config, 915 + }; 916 + 917 + static u32 sfp_hwmon_vcc_config[] = { 918 + HWMON_I_INPUT | 919 + HWMON_I_MAX | HWMON_I_MIN | 920 + HWMON_I_MAX_ALARM | HWMON_I_MIN_ALARM | 921 + HWMON_I_CRIT | HWMON_I_LCRIT | 922 + HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM, 923 + 0, 924 + }; 925 + 926 + static const struct hwmon_channel_info sfp_hwmon_vcc_channel_info = { 927 + .type = hwmon_in, 928 + .config = sfp_hwmon_vcc_config, 929 + }; 930 + 931 + static u32 sfp_hwmon_bias_config[] = { 932 + HWMON_C_INPUT | 933 + HWMON_C_MAX | HWMON_C_MIN | 934 + HWMON_C_MAX_ALARM | HWMON_C_MIN_ALARM | 935 + HWMON_C_CRIT | HWMON_C_LCRIT | 936 + HWMON_C_CRIT_ALARM | HWMON_C_LCRIT_ALARM, 937 + 0, 938 + }; 939 + 940 + static const struct hwmon_channel_info sfp_hwmon_bias_channel_info = { 941 + .type = hwmon_curr, 942 + .config = sfp_hwmon_bias_config, 943 + }; 944 + 945 + static u32 sfp_hwmon_power_config[] = { 946 + /* Transmit power */ 947 + HWMON_P_INPUT | 948 + HWMON_P_MAX | HWMON_P_MIN | 949 + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | 950 + HWMON_P_CRIT | HWMON_P_LCRIT | 951 + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM, 952 + /* Receive power */ 953 + HWMON_P_INPUT | 954 + HWMON_P_MAX | HWMON_P_MIN | 955 + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | 956 + HWMON_P_CRIT | HWMON_P_LCRIT | 957 + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM, 958 + 0, 959 + }; 960 + 961 + static const struct hwmon_channel_info sfp_hwmon_power_channel_info = { 962 + .type = hwmon_power, 963 + .config = sfp_hwmon_power_config, 964 + }; 965 + 966 + static const struct hwmon_channel_info *sfp_hwmon_info[] = { 967 + &sfp_hwmon_chip, 968 + &sfp_hwmon_vcc_channel_info, 969 + &sfp_hwmon_temp_channel_info, 970 + &sfp_hwmon_bias_channel_info, 971 + &sfp_hwmon_power_channel_info, 972 + NULL, 973 + }; 974 + 975 + static const struct hwmon_chip_info sfp_hwmon_chip_info = { 976 + .ops = &sfp_hwmon_ops, 977 + .info = sfp_hwmon_info, 978 + }; 979 + 980 + static int sfp_hwmon_insert(struct sfp *sfp) 981 + { 982 + int err, i; 983 + 984 + if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) 985 + return 0; 986 + 987 + if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) 988 + return 0; 989 + 990 + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) 991 + /* This driver in general does not support address 992 + * change. 993 + */ 994 + return 0; 995 + 996 + err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); 997 + if (err < 0) 998 + return err; 999 + 1000 + sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL); 1001 + if (!sfp->hwmon_name) 1002 + return -ENODEV; 1003 + 1004 + for (i = 0; sfp->hwmon_name[i]; i++) 1005 + if (hwmon_is_bad_char(sfp->hwmon_name[i])) 1006 + sfp->hwmon_name[i] = '_'; 1007 + 1008 + sfp->hwmon_dev = hwmon_device_register_with_info(sfp->dev, 1009 + sfp->hwmon_name, sfp, 1010 + &sfp_hwmon_chip_info, 1011 + NULL); 1012 + 1013 + return PTR_ERR_OR_ZERO(sfp->hwmon_dev); 1014 + } 1015 + 1016 + static void sfp_hwmon_remove(struct sfp *sfp) 1017 + { 1018 + hwmon_device_unregister(sfp->hwmon_dev); 1019 + kfree(sfp->hwmon_name); 1020 + } 1021 + #else 1022 + static int sfp_hwmon_insert(struct sfp *sfp) 1023 + { 1024 + return 0; 1025 + } 1026 + 1027 + static void sfp_hwmon_remove(struct sfp *sfp) 1028 + { 1029 + } 1030 + #endif 326 1031 327 1032 /* Helpers */ 328 1033 static void sfp_module_tx_disable(struct sfp *sfp) ··· 1357 636 dev_warn(sfp->dev, 1358 637 "module address swap to access page 0xA2 is not supported.\n"); 1359 638 639 + ret = sfp_hwmon_insert(sfp); 640 + if (ret < 0) 641 + return ret; 642 + 1360 643 ret = sfp_module_insert(sfp->sfp_bus, &sfp->id); 1361 644 if (ret < 0) 1362 645 return ret; ··· 1371 646 static void sfp_sm_mod_remove(struct sfp *sfp) 1372 647 { 1373 648 sfp_module_remove(sfp->sfp_bus); 649 + 650 + sfp_hwmon_remove(sfp); 1374 651 1375 652 if (sfp->mod_phy) 1376 653 sfp_sm_phy_detach(sfp);
+71 -1
include/linux/sfp.h
··· 231 231 struct sfp_eeprom_ext ext; 232 232 } __packed; 233 233 234 + struct sfp_diag { 235 + __be16 temp_high_alarm; 236 + __be16 temp_low_alarm; 237 + __be16 temp_high_warn; 238 + __be16 temp_low_warn; 239 + __be16 volt_high_alarm; 240 + __be16 volt_low_alarm; 241 + __be16 volt_high_warn; 242 + __be16 volt_low_warn; 243 + __be16 bias_high_alarm; 244 + __be16 bias_low_alarm; 245 + __be16 bias_high_warn; 246 + __be16 bias_low_warn; 247 + __be16 txpwr_high_alarm; 248 + __be16 txpwr_low_alarm; 249 + __be16 txpwr_high_warn; 250 + __be16 txpwr_low_warn; 251 + __be16 rxpwr_high_alarm; 252 + __be16 rxpwr_low_alarm; 253 + __be16 rxpwr_high_warn; 254 + __be16 rxpwr_low_warn; 255 + __be16 laser_temp_high_alarm; 256 + __be16 laser_temp_low_alarm; 257 + __be16 laser_temp_high_warn; 258 + __be16 laser_temp_low_warn; 259 + __be16 tec_cur_high_alarm; 260 + __be16 tec_cur_low_alarm; 261 + __be16 tec_cur_high_warn; 262 + __be16 tec_cur_low_warn; 263 + __be32 cal_rxpwr4; 264 + __be32 cal_rxpwr3; 265 + __be32 cal_rxpwr2; 266 + __be32 cal_rxpwr1; 267 + __be32 cal_rxpwr0; 268 + __be16 cal_txi_slope; 269 + __be16 cal_txi_offset; 270 + __be16 cal_txpwr_slope; 271 + __be16 cal_txpwr_offset; 272 + __be16 cal_t_slope; 273 + __be16 cal_t_offset; 274 + __be16 cal_v_slope; 275 + __be16 cal_v_offset; 276 + } __packed; 277 + 234 278 /* SFP EEPROM registers */ 235 279 enum { 236 280 SFP_PHYS_ID = 0x00, ··· 428 384 SFP_TEC_CUR = 0x6c, 429 385 430 386 SFP_STATUS = 0x6e, 431 - SFP_ALARM = 0x70, 387 + SFP_ALARM0 = 0x70, 388 + SFP_ALARM0_TEMP_HIGH = BIT(7), 389 + SFP_ALARM0_TEMP_LOW = BIT(6), 390 + SFP_ALARM0_VCC_HIGH = BIT(5), 391 + SFP_ALARM0_VCC_LOW = BIT(4), 392 + SFP_ALARM0_TX_BIAS_HIGH = BIT(3), 393 + SFP_ALARM0_TX_BIAS_LOW = BIT(2), 394 + SFP_ALARM0_TXPWR_HIGH = BIT(1), 395 + SFP_ALARM0_TXPWR_LOW = BIT(0), 396 + 397 + SFP_ALARM1 = 0x71, 398 + SFP_ALARM1_RXPWR_HIGH = BIT(7), 399 + SFP_ALARM1_RXPWR_LOW = BIT(6), 400 + 401 + SFP_WARN0 = 0x74, 402 + SFP_WARN0_TEMP_HIGH = BIT(7), 403 + SFP_WARN0_TEMP_LOW = BIT(6), 404 + SFP_WARN0_VCC_HIGH = BIT(5), 405 + SFP_WARN0_VCC_LOW = BIT(4), 406 + SFP_WARN0_TX_BIAS_HIGH = BIT(3), 407 + SFP_WARN0_TX_BIAS_LOW = BIT(2), 408 + SFP_WARN0_TXPWR_HIGH = BIT(1), 409 + SFP_WARN0_TXPWR_LOW = BIT(0), 410 + 411 + SFP_WARN1 = 0x75, 412 + SFP_WARN1_RXPWR_HIGH = BIT(7), 413 + SFP_WARN1_RXPWR_LOW = BIT(6), 432 414 433 415 SFP_EXT_STATUS = 0x76, 434 416 SFP_VSL = 0x78,