Skip to content

Commit f4190b0

Browse files
pameruosofrank-w
authored andcommitted
net: phy: as21xxx: add hwmon temperature sensor
AS21xxx firmware exposes the on-chip temperature monitor through the CFG_PARAM/DIRECT IPC command. The monitor needs to be configured and started before a temperature sample is available. Once started, the GET operation returns the current temperature as a signed 16.16 Celsius value in response words 1 and 2. Add optional hwmon support and expose the temperature as temp1_input in millidegrees Celsius. If the firmware rejects the temperature monitor setup, keep probing the PHY and simply skip hwmon registration. Signed-off-by: Pietro Ameruoso <p.ameruoso@live.it>
1 parent 87a9c23 commit f4190b0

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

drivers/net/phy/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ comment "MII PHY device drivers"
8080

8181
config AS21XXX_PHY
8282
tristate "Aeonsemi AS21xxx PHYs"
83+
depends on HWMON || HWMON=n
8384
help
8485
Currently supports the Aeonsemi AS21xxx PHY.
8586

drivers/net/phy/as21xxx.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <linux/bitfield.h>
99
#include <linux/firmware.h>
10+
#include <linux/hwmon.h>
1011
#include <linux/module.h>
1112
#include <linux/of.h>
1213
#include <linux/phy.h>
@@ -113,7 +114,10 @@
113114
#define IPC_CFG_PARAM_DIRECT_WOL 0x12
114115

115116
/* Sub command of CMD_TEMP_MON */
117+
#define IPC_CMD_TEMP_MON_START 0x1
118+
#define IPC_CMD_TEMP_MON_CFG 0x3
116119
#define IPC_CMD_TEMP_MON_GET 0x4
120+
#define IPC_CMD_TEMP_MON_CONTINUOUS 0x1
117121

118122
#define AS21XXX_MDIO_AN_C22 0xffe0
119123
#define AS21XXX_AN_STATES1 0x8005
@@ -603,6 +607,113 @@ static int aeon_dpc_ra_enable(struct phy_device *phydev)
603607
sizeof(data), NULL);
604608
}
605609

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+
606717
static int as21xxx_probe(struct phy_device *phydev)
607718
{
608719
struct as21xxx_priv *priv;
@@ -657,6 +768,10 @@ static int as21xxx_probe(struct phy_device *phydev)
657768
if (ret)
658769
return ret;
659770

771+
ret = as21xxx_hwmon_probe(phydev);
772+
if (ret)
773+
return ret;
774+
660775
/* Enable PTP clk if not already Enabled */
661776
return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
662777
VEND1_PTP_CLK_EN);

0 commit comments

Comments
 (0)