|
9 | 9 | #include <linux/ethtool_netlink.h>
|
10 | 10 | #include <linux/marvell_phy.h>
|
11 | 11 | #include <linux/phy.h>
|
| 12 | +#include <linux/hwmon.h> |
12 | 13 |
|
13 | 14 | #define PHY_ID_88Q2220_REVB0 (MARVELL_PHY_ID_88Q2220 | 0x1)
|
14 | 15 |
|
|
37 | 38 | #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL 32787
|
38 | 39 | #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS 0x0800
|
39 | 40 |
|
| 41 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR1 32833 |
| 42 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT 0x0001 |
| 43 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT 0x0040 |
| 44 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT_EN 0x0080 |
| 45 | + |
| 46 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR2 32834 |
| 47 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK 0xc000 |
| 48 | + |
| 49 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR3 32835 |
| 50 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK 0xff00 |
| 51 | +#define MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK 0x00ff |
| 52 | + |
40 | 53 | #define MDIO_MMD_PCS_MV_100BT1_STAT1 33032
|
41 | 54 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR 0x00ff
|
42 | 55 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER 0x0100
|
@@ -493,6 +506,138 @@ static int mv88q2xxx_resume(struct phy_device *phydev)
|
493 | 506 | MDIO_CTRL1_LPOWER);
|
494 | 507 | }
|
495 | 508 |
|
| 509 | +#if IS_ENABLED(CONFIG_HWMON) |
| 510 | +static const struct hwmon_channel_info * const mv88q2xxx_hwmon_info[] = { |
| 511 | + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_ALARM), |
| 512 | + NULL |
| 513 | +}; |
| 514 | + |
| 515 | +static umode_t mv88q2xxx_hwmon_is_visible(const void *data, |
| 516 | + enum hwmon_sensor_types type, |
| 517 | + u32 attr, int channel) |
| 518 | +{ |
| 519 | + switch (attr) { |
| 520 | + case hwmon_temp_input: |
| 521 | + return 0444; |
| 522 | + case hwmon_temp_max: |
| 523 | + return 0644; |
| 524 | + case hwmon_temp_alarm: |
| 525 | + return 0444; |
| 526 | + default: |
| 527 | + return 0; |
| 528 | + } |
| 529 | +} |
| 530 | + |
| 531 | +static int mv88q2xxx_hwmon_read(struct device *dev, |
| 532 | + enum hwmon_sensor_types type, |
| 533 | + u32 attr, int channel, long *val) |
| 534 | +{ |
| 535 | + struct phy_device *phydev = dev_get_drvdata(dev); |
| 536 | + int ret; |
| 537 | + |
| 538 | + switch (attr) { |
| 539 | + case hwmon_temp_input: |
| 540 | + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
| 541 | + MDIO_MMD_PCS_MV_TEMP_SENSOR3); |
| 542 | + if (ret < 0) |
| 543 | + return ret; |
| 544 | + |
| 545 | + ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK, ret); |
| 546 | + *val = (ret - 75) * 1000; |
| 547 | + return 0; |
| 548 | + case hwmon_temp_max: |
| 549 | + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
| 550 | + MDIO_MMD_PCS_MV_TEMP_SENSOR3); |
| 551 | + if (ret < 0) |
| 552 | + return ret; |
| 553 | + |
| 554 | + ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
| 555 | + ret); |
| 556 | + *val = (ret - 75) * 1000; |
| 557 | + return 0; |
| 558 | + case hwmon_temp_alarm: |
| 559 | + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
| 560 | + MDIO_MMD_PCS_MV_TEMP_SENSOR1); |
| 561 | + if (ret < 0) |
| 562 | + return ret; |
| 563 | + |
| 564 | + *val = !!(ret & MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT); |
| 565 | + return 0; |
| 566 | + default: |
| 567 | + return -EOPNOTSUPP; |
| 568 | + } |
| 569 | +} |
| 570 | + |
| 571 | +static int mv88q2xxx_hwmon_write(struct device *dev, |
| 572 | + enum hwmon_sensor_types type, u32 attr, |
| 573 | + int channel, long val) |
| 574 | +{ |
| 575 | + struct phy_device *phydev = dev_get_drvdata(dev); |
| 576 | + |
| 577 | + switch (attr) { |
| 578 | + case hwmon_temp_max: |
| 579 | + clamp_val(val, -75000, 180000); |
| 580 | + val = (val / 1000) + 75; |
| 581 | + val = FIELD_PREP(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
| 582 | + val); |
| 583 | + return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
| 584 | + MDIO_MMD_PCS_MV_TEMP_SENSOR3, |
| 585 | + MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
| 586 | + val); |
| 587 | + default: |
| 588 | + return -EOPNOTSUPP; |
| 589 | + } |
| 590 | +} |
| 591 | + |
| 592 | +static const struct hwmon_ops mv88q2xxx_hwmon_hwmon_ops = { |
| 593 | + .is_visible = mv88q2xxx_hwmon_is_visible, |
| 594 | + .read = mv88q2xxx_hwmon_read, |
| 595 | + .write = mv88q2xxx_hwmon_write, |
| 596 | +}; |
| 597 | + |
| 598 | +static const struct hwmon_chip_info mv88q2xxx_hwmon_chip_info = { |
| 599 | + .ops = &mv88q2xxx_hwmon_hwmon_ops, |
| 600 | + .info = mv88q2xxx_hwmon_info, |
| 601 | +}; |
| 602 | + |
| 603 | +static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) |
| 604 | +{ |
| 605 | + struct device *dev = &phydev->mdio.dev; |
| 606 | + struct device *hwmon; |
| 607 | + char *hwmon_name; |
| 608 | + int ret; |
| 609 | + |
| 610 | + /* Enable temperature sense */ |
| 611 | + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TEMP_SENSOR2, |
| 612 | + MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK, 0); |
| 613 | + if (ret < 0) |
| 614 | + return ret; |
| 615 | + |
| 616 | + hwmon_name = devm_hwmon_sanitize_name(dev, dev_name(dev)); |
| 617 | + if (IS_ERR(hwmon_name)) |
| 618 | + return PTR_ERR(hwmon_name); |
| 619 | + |
| 620 | + hwmon = devm_hwmon_device_register_with_info(dev, |
| 621 | + hwmon_name, |
| 622 | + phydev, |
| 623 | + &mv88q2xxx_hwmon_chip_info, |
| 624 | + NULL); |
| 625 | + |
| 626 | + return PTR_ERR_OR_ZERO(hwmon); |
| 627 | +} |
| 628 | + |
| 629 | +#else |
| 630 | +static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) |
| 631 | +{ |
| 632 | + return 0; |
| 633 | +} |
| 634 | +#endif |
| 635 | + |
| 636 | +static int mv88q2xxx_probe(struct phy_device *phydev) |
| 637 | +{ |
| 638 | + return mv88q2xxx_hwmon_probe(phydev); |
| 639 | +} |
| 640 | + |
496 | 641 | static int mv88q222x_soft_reset(struct phy_device *phydev)
|
497 | 642 | {
|
498 | 643 | int ret;
|
@@ -587,6 +732,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
|
587 | 732 | {
|
588 | 733 | PHY_ID_MATCH_EXACT(PHY_ID_88Q2220_REVB0),
|
589 | 734 | .name = "mv88q2220",
|
| 735 | + .probe = mv88q2xxx_probe, |
590 | 736 | .get_features = mv88q2xxx_get_features,
|
591 | 737 | .config_aneg = mv88q222x_config_aneg,
|
592 | 738 | .aneg_done = genphy_c45_aneg_done,
|
|
0 commit comments