1 // SPDX-License-Identifier: GPL-2.0
2 /* HWMON driver for Aquantia PHY
4 * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
5 * Author: Andrew Lunn <andrew@lunn.ch>
6 * Author: Heiner Kallweit <hkallweit1@gmail.com>
10 #include <linux/device.h>
11 #include <linux/ctype.h>
12 #include <linux/hwmon.h>
16 #if IS_REACHABLE(CONFIG_HWMON)
18 static umode_t
aqr_hwmon_is_visible(const void *data
,
19 enum hwmon_sensor_types type
,
20 u32 attr
, int channel
)
22 if (type
!= hwmon_temp
)
26 case hwmon_temp_input
:
27 case hwmon_temp_min_alarm
:
28 case hwmon_temp_max_alarm
:
29 case hwmon_temp_lcrit_alarm
:
30 case hwmon_temp_crit_alarm
:
34 case hwmon_temp_lcrit
:
42 static int aqr_hwmon_get(struct phy_device
*phydev
, int reg
, long *value
)
44 int temp
= phy_read_mmd(phydev
, MDIO_MMD_VEND1
, reg
);
49 /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */
50 *value
= (s16
)temp
* 1000 / 256;
55 static int aqr_hwmon_set(struct phy_device
*phydev
, int reg
, long value
)
59 if (value
>= 128000 || value
< -128000)
62 temp
= value
* 256 / 1000;
64 /* temp is in s16 range and we're interested in lower 16 bits only */
65 return phy_write_mmd(phydev
, MDIO_MMD_VEND1
, reg
, (u16
)temp
);
68 static int aqr_hwmon_test_bit(struct phy_device
*phydev
, int reg
, int bit
)
70 int val
= phy_read_mmd(phydev
, MDIO_MMD_VEND1
, reg
);
78 static int aqr_hwmon_status1(struct phy_device
*phydev
, int bit
, long *value
)
80 int val
= aqr_hwmon_test_bit(phydev
, VEND1_GENERAL_STAT1
, bit
);
90 static int aqr_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
91 u32 attr
, int channel
, long *value
)
93 struct phy_device
*phydev
= dev_get_drvdata(dev
);
96 if (type
!= hwmon_temp
)
100 case hwmon_temp_input
:
101 reg
= aqr_hwmon_test_bit(phydev
, VEND1_THERMAL_STAT2
,
102 VEND1_THERMAL_STAT2_VALID
);
108 return aqr_hwmon_get(phydev
, VEND1_THERMAL_STAT1
, value
);
110 case hwmon_temp_lcrit
:
111 return aqr_hwmon_get(phydev
, VEND1_THERMAL_PROV_LOW_TEMP_FAIL
,
114 return aqr_hwmon_get(phydev
, VEND1_THERMAL_PROV_LOW_TEMP_WARN
,
117 return aqr_hwmon_get(phydev
, VEND1_THERMAL_PROV_HIGH_TEMP_WARN
,
119 case hwmon_temp_crit
:
120 return aqr_hwmon_get(phydev
, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL
,
122 case hwmon_temp_lcrit_alarm
:
123 return aqr_hwmon_status1(phydev
,
124 VEND1_GENERAL_STAT1_LOW_TEMP_FAIL
,
126 case hwmon_temp_min_alarm
:
127 return aqr_hwmon_status1(phydev
,
128 VEND1_GENERAL_STAT1_LOW_TEMP_WARN
,
130 case hwmon_temp_max_alarm
:
131 return aqr_hwmon_status1(phydev
,
132 VEND1_GENERAL_STAT1_HIGH_TEMP_WARN
,
134 case hwmon_temp_crit_alarm
:
135 return aqr_hwmon_status1(phydev
,
136 VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL
,
143 static int aqr_hwmon_write(struct device
*dev
, enum hwmon_sensor_types type
,
144 u32 attr
, int channel
, long value
)
146 struct phy_device
*phydev
= dev_get_drvdata(dev
);
148 if (type
!= hwmon_temp
)
152 case hwmon_temp_lcrit
:
153 return aqr_hwmon_set(phydev
, VEND1_THERMAL_PROV_LOW_TEMP_FAIL
,
156 return aqr_hwmon_set(phydev
, VEND1_THERMAL_PROV_LOW_TEMP_WARN
,
159 return aqr_hwmon_set(phydev
, VEND1_THERMAL_PROV_HIGH_TEMP_WARN
,
161 case hwmon_temp_crit
:
162 return aqr_hwmon_set(phydev
, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL
,
169 static const struct hwmon_ops aqr_hwmon_ops
= {
170 .is_visible
= aqr_hwmon_is_visible
,
171 .read
= aqr_hwmon_read
,
172 .write
= aqr_hwmon_write
,
175 static u32 aqr_hwmon_chip_config
[] = {
180 static const struct hwmon_channel_info aqr_hwmon_chip
= {
182 .config
= aqr_hwmon_chip_config
,
185 static u32 aqr_hwmon_temp_config
[] = {
187 HWMON_T_MAX
| HWMON_T_MIN
|
188 HWMON_T_MAX_ALARM
| HWMON_T_MIN_ALARM
|
189 HWMON_T_CRIT
| HWMON_T_LCRIT
|
190 HWMON_T_CRIT_ALARM
| HWMON_T_LCRIT_ALARM
,
194 static const struct hwmon_channel_info aqr_hwmon_temp
= {
196 .config
= aqr_hwmon_temp_config
,
199 static const struct hwmon_channel_info
* const aqr_hwmon_info
[] = {
205 static const struct hwmon_chip_info aqr_hwmon_chip_info
= {
206 .ops
= &aqr_hwmon_ops
,
207 .info
= aqr_hwmon_info
,
210 int aqr_hwmon_probe(struct phy_device
*phydev
)
212 struct device
*dev
= &phydev
->mdio
.dev
;
213 struct device
*hwmon_dev
;
217 hwmon_name
= devm_kstrdup(dev
, dev_name(dev
), GFP_KERNEL
);
221 for (i
= j
= 0; hwmon_name
[i
]; i
++) {
222 if (isalnum(hwmon_name
[i
])) {
224 hwmon_name
[j
] = hwmon_name
[i
];
228 hwmon_name
[j
] = '\0';
230 hwmon_dev
= devm_hwmon_device_register_with_info(dev
, hwmon_name
,
231 phydev
, &aqr_hwmon_chip_info
, NULL
);
233 return PTR_ERR_OR_ZERO(hwmon_dev
);