|
7 | 7 |
|
8 | 8 | #include <linux/bitfield.h> |
9 | 9 | #include <linux/firmware.h> |
| 10 | +#include <linux/hwmon.h> |
10 | 11 | #include <linux/module.h> |
11 | 12 | #include <linux/of.h> |
12 | 13 | #include <linux/phy.h> |
|
113 | 114 | #define IPC_CFG_PARAM_DIRECT_WOL 0x12 |
114 | 115 |
|
115 | 116 | /* Sub command of CMD_TEMP_MON */ |
| 117 | +#define IPC_CMD_TEMP_MON_START 0x1 |
| 118 | +#define IPC_CMD_TEMP_MON_CFG 0x3 |
116 | 119 | #define IPC_CMD_TEMP_MON_GET 0x4 |
| 120 | +#define IPC_CMD_TEMP_MON_CONTINUOUS 0x1 |
117 | 121 |
|
118 | 122 | #define AS21XXX_MDIO_AN_C22 0xffe0 |
119 | 123 | #define AS21XXX_AN_STATES1 0x8005 |
@@ -603,6 +607,113 @@ static int aeon_dpc_ra_enable(struct phy_device *phydev) |
603 | 607 | sizeof(data), NULL); |
604 | 608 | } |
605 | 609 |
|
| 610 | +#if IS_ENABLED(CONFIG_HWMON) |
| 611 | +static int aeon_temp_monitor_cmd(struct phy_device *phydev, u16 sub_cmd, |
| 612 | + u16 param, u16 *ret_data) |
| 613 | +{ |
| 614 | + u16 data[4]; |
| 615 | + |
| 616 | + data[0] = IPC_CFG_PARAM_DIRECT; |
| 617 | + data[1] = IPC_CFG_PARAM_DIRECT_TEMP_MON; |
| 618 | + data[2] = sub_cmd; |
| 619 | + data[3] = param; |
| 620 | + |
| 621 | + return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, |
| 622 | + sizeof(data), ret_data); |
| 623 | +} |
| 624 | + |
| 625 | +static int aeon_temp_monitor_enable(struct phy_device *phydev) |
| 626 | +{ |
| 627 | + int ret; |
| 628 | + |
| 629 | + ret = aeon_temp_monitor_cmd(phydev, IPC_CMD_TEMP_MON_CFG, |
| 630 | + IPC_CMD_TEMP_MON_CONTINUOUS, NULL); |
| 631 | + if (ret < 0) |
| 632 | + return ret; |
| 633 | + |
| 634 | + return aeon_temp_monitor_cmd(phydev, IPC_CMD_TEMP_MON_START, 0, NULL); |
| 635 | +} |
| 636 | + |
| 637 | +static int aeon_get_temperature(struct phy_device *phydev, long *temp) |
| 638 | +{ |
| 639 | + u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS] = { 0 }; |
| 640 | + s32 temp_fixed; |
| 641 | + int ret; |
| 642 | + |
| 643 | + ret = aeon_temp_monitor_cmd(phydev, IPC_CMD_TEMP_MON_GET, 0, ret_data); |
| 644 | + if (ret < 0) |
| 645 | + return ret; |
| 646 | + if (ret < 3 * sizeof(u16)) |
| 647 | + return -ENODATA; |
| 648 | + |
| 649 | + temp_fixed = (s32)((u32)ret_data[1] | ((u32)ret_data[2] << 16)); |
| 650 | + *temp = ((s64)temp_fixed * 1000) / 65536; |
| 651 | + |
| 652 | + return 0; |
| 653 | +} |
| 654 | + |
| 655 | +static int as21xxx_hwmon_read(struct device *dev, |
| 656 | + enum hwmon_sensor_types type, |
| 657 | + u32 attr, int channel, long *value) |
| 658 | +{ |
| 659 | + struct phy_device *phydev = dev_get_drvdata(dev); |
| 660 | + |
| 661 | + if (type != hwmon_temp || attr != hwmon_temp_input) |
| 662 | + return -EOPNOTSUPP; |
| 663 | + |
| 664 | + return aeon_get_temperature(phydev, value); |
| 665 | +} |
| 666 | + |
| 667 | +static umode_t as21xxx_hwmon_is_visible(const void *data, |
| 668 | + enum hwmon_sensor_types type, |
| 669 | + u32 attr, int channel) |
| 670 | +{ |
| 671 | + if (type == hwmon_temp && attr == hwmon_temp_input) |
| 672 | + return 0444; |
| 673 | + |
| 674 | + return 0; |
| 675 | +} |
| 676 | + |
| 677 | +static const struct hwmon_channel_info * const as21xxx_hwmon_info[] = { |
| 678 | + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), |
| 679 | + NULL |
| 680 | +}; |
| 681 | + |
| 682 | +static const struct hwmon_ops as21xxx_hwmon_ops = { |
| 683 | + .is_visible = as21xxx_hwmon_is_visible, |
| 684 | + .read = as21xxx_hwmon_read, |
| 685 | +}; |
| 686 | + |
| 687 | +static const struct hwmon_chip_info as21xxx_hwmon_chip_info = { |
| 688 | + .ops = &as21xxx_hwmon_ops, |
| 689 | + .info = as21xxx_hwmon_info, |
| 690 | +}; |
| 691 | + |
| 692 | +static int as21xxx_hwmon_probe(struct phy_device *phydev) |
| 693 | +{ |
| 694 | + struct device *dev = &phydev->mdio.dev; |
| 695 | + struct device *hwmon_dev; |
| 696 | + int ret; |
| 697 | + |
| 698 | + ret = aeon_temp_monitor_enable(phydev); |
| 699 | + if (ret < 0) { |
| 700 | + phydev_warn(phydev, |
| 701 | + "failed to enable temperature monitor: %d\n", ret); |
| 702 | + return 0; |
| 703 | + } |
| 704 | + |
| 705 | + hwmon_dev = devm_hwmon_device_register_with_info(dev, NULL, phydev, |
| 706 | + &as21xxx_hwmon_chip_info, NULL); |
| 707 | + |
| 708 | + return PTR_ERR_OR_ZERO(hwmon_dev); |
| 709 | +} |
| 710 | +#else |
| 711 | +static int as21xxx_hwmon_probe(struct phy_device *phydev) |
| 712 | +{ |
| 713 | + return 0; |
| 714 | +} |
| 715 | +#endif |
| 716 | + |
606 | 717 | static int as21xxx_probe(struct phy_device *phydev) |
607 | 718 | { |
608 | 719 | struct as21xxx_priv *priv; |
@@ -657,6 +768,10 @@ static int as21xxx_probe(struct phy_device *phydev) |
657 | 768 | if (ret) |
658 | 769 | return ret; |
659 | 770 |
|
| 771 | + ret = as21xxx_hwmon_probe(phydev); |
| 772 | + if (ret) |
| 773 | + return ret; |
| 774 | + |
660 | 775 | /* Enable PTP clk if not already Enabled */ |
661 | 776 | return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, |
662 | 777 | VEND1_PTP_CLK_EN); |
|
0 commit comments