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

net: phy: marvell-88q2xxx: add support for temperature sensor

Marvell 88q2xxx devices have an inbuilt temperature sensor. Add hwmon
support for this sensor.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Dimitri Fedrau <dima.fedrau@gmail.com>
Link: https://lore.kernel.org/r/20240218075753.18067-9-dima.fedrau@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Dimitri Fedrau and committed by
Jakub Kicinski
a557a92e 5f9f361a

+147
+1
drivers/net/phy/Kconfig
··· 232 232 233 233 config MARVELL_88Q2XXX_PHY 234 234 tristate "Marvell 88Q2XXX PHY" 235 + depends on HWMON || HWMON=n 235 236 help 236 237 Support for the Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet 237 238 PHYs.
+146
drivers/net/phy/marvell-88q2xxx.c
··· 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 36 38 37 #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL 32787 39 38 #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS 0x0800 39 + 40 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR1 32833 41 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT 0x0001 42 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT 0x0040 43 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT_EN 0x0080 44 + 45 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR2 32834 46 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK 0xc000 47 + 48 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR3 32835 49 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK 0xff00 50 + #define MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK 0x00ff 40 51 41 52 #define MDIO_MMD_PCS_MV_100BT1_STAT1 33032 42 53 #define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR 0x00ff ··· 506 493 MDIO_CTRL1_LPOWER); 507 494 } 508 495 496 + #if IS_ENABLED(CONFIG_HWMON) 497 + static const struct hwmon_channel_info * const mv88q2xxx_hwmon_info[] = { 498 + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_ALARM), 499 + NULL 500 + }; 501 + 502 + static umode_t mv88q2xxx_hwmon_is_visible(const void *data, 503 + enum hwmon_sensor_types type, 504 + u32 attr, int channel) 505 + { 506 + switch (attr) { 507 + case hwmon_temp_input: 508 + return 0444; 509 + case hwmon_temp_max: 510 + return 0644; 511 + case hwmon_temp_alarm: 512 + return 0444; 513 + default: 514 + return 0; 515 + } 516 + } 517 + 518 + static int mv88q2xxx_hwmon_read(struct device *dev, 519 + enum hwmon_sensor_types type, 520 + u32 attr, int channel, long *val) 521 + { 522 + struct phy_device *phydev = dev_get_drvdata(dev); 523 + int ret; 524 + 525 + switch (attr) { 526 + case hwmon_temp_input: 527 + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 528 + MDIO_MMD_PCS_MV_TEMP_SENSOR3); 529 + if (ret < 0) 530 + return ret; 531 + 532 + ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK, ret); 533 + *val = (ret - 75) * 1000; 534 + return 0; 535 + case hwmon_temp_max: 536 + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 537 + MDIO_MMD_PCS_MV_TEMP_SENSOR3); 538 + if (ret < 0) 539 + return ret; 540 + 541 + ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, 542 + ret); 543 + *val = (ret - 75) * 1000; 544 + return 0; 545 + case hwmon_temp_alarm: 546 + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 547 + MDIO_MMD_PCS_MV_TEMP_SENSOR1); 548 + if (ret < 0) 549 + return ret; 550 + 551 + *val = !!(ret & MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT); 552 + return 0; 553 + default: 554 + return -EOPNOTSUPP; 555 + } 556 + } 557 + 558 + static int mv88q2xxx_hwmon_write(struct device *dev, 559 + enum hwmon_sensor_types type, u32 attr, 560 + int channel, long val) 561 + { 562 + struct phy_device *phydev = dev_get_drvdata(dev); 563 + 564 + switch (attr) { 565 + case hwmon_temp_max: 566 + clamp_val(val, -75000, 180000); 567 + val = (val / 1000) + 75; 568 + val = FIELD_PREP(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, 569 + val); 570 + return phy_modify_mmd(phydev, MDIO_MMD_PCS, 571 + MDIO_MMD_PCS_MV_TEMP_SENSOR3, 572 + MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, 573 + val); 574 + default: 575 + return -EOPNOTSUPP; 576 + } 577 + } 578 + 579 + static const struct hwmon_ops mv88q2xxx_hwmon_hwmon_ops = { 580 + .is_visible = mv88q2xxx_hwmon_is_visible, 581 + .read = mv88q2xxx_hwmon_read, 582 + .write = mv88q2xxx_hwmon_write, 583 + }; 584 + 585 + static const struct hwmon_chip_info mv88q2xxx_hwmon_chip_info = { 586 + .ops = &mv88q2xxx_hwmon_hwmon_ops, 587 + .info = mv88q2xxx_hwmon_info, 588 + }; 589 + 590 + static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) 591 + { 592 + struct device *dev = &phydev->mdio.dev; 593 + struct device *hwmon; 594 + char *hwmon_name; 595 + int ret; 596 + 597 + /* Enable temperature sense */ 598 + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TEMP_SENSOR2, 599 + MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK, 0); 600 + if (ret < 0) 601 + return ret; 602 + 603 + hwmon_name = devm_hwmon_sanitize_name(dev, dev_name(dev)); 604 + if (IS_ERR(hwmon_name)) 605 + return PTR_ERR(hwmon_name); 606 + 607 + hwmon = devm_hwmon_device_register_with_info(dev, 608 + hwmon_name, 609 + phydev, 610 + &mv88q2xxx_hwmon_chip_info, 611 + NULL); 612 + 613 + return PTR_ERR_OR_ZERO(hwmon); 614 + } 615 + 616 + #else 617 + static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) 618 + { 619 + return 0; 620 + } 621 + #endif 622 + 623 + static int mv88q2xxx_probe(struct phy_device *phydev) 624 + { 625 + return mv88q2xxx_hwmon_probe(phydev); 626 + } 627 + 509 628 static int mv88q222x_soft_reset(struct phy_device *phydev) 510 629 { 511 630 int ret; ··· 732 587 { 733 588 PHY_ID_MATCH_EXACT(PHY_ID_88Q2220_REVB0), 734 589 .name = "mv88q2220", 590 + .probe = mv88q2xxx_probe, 735 591 .get_features = mv88q2xxx_get_features, 736 592 .config_aneg = mv88q222x_config_aneg, 737 593 .aneg_done = genphy_c45_aneg_done,