Skip to content

Commit 0d3ad85

Browse files
Russell Kingdavem330
authored andcommitted
net: phy: marvell10g: add thermal hwmon device
Add a thermal monitoring device for the Marvell 88x3310, which updates once a second. We also need to hook into the suspend/resume mechanism to ensure that the thermal monitoring is reconfigured when we resume. Suggested-by: Andrew Lunn <[email protected]> Signed-off-by: Russell King <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent bfacfb4 commit 0d3ad85

File tree

1 file changed

+182
-2
lines changed

1 file changed

+182
-2
lines changed

drivers/net/phy/marvell10g.c

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
* If both the fiber and copper ports are connected, the first to gain
2222
* link takes priority and the other port is completely locked out.
2323
*/
24-
#include <linux/phy.h>
24+
#include <linux/ctype.h>
25+
#include <linux/hwmon.h>
2526
#include <linux/marvell_phy.h>
27+
#include <linux/phy.h>
2628

2729
enum {
2830
MV_PCS_BASE_T = 0x0000,
@@ -40,6 +42,19 @@ enum {
4042
*/
4143
MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */
4244
MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */
45+
46+
/* Vendor2 MMD registers */
47+
MV_V2_TEMP_CTRL = 0xf08a,
48+
MV_V2_TEMP_CTRL_MASK = 0xc000,
49+
MV_V2_TEMP_CTRL_SAMPLE = 0x0000,
50+
MV_V2_TEMP_CTRL_DISABLE = 0xc000,
51+
MV_V2_TEMP = 0xf08c,
52+
MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */
53+
};
54+
55+
struct mv3310_priv {
56+
struct device *hwmon_dev;
57+
char *hwmon_name;
4358
};
4459

4560
static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
@@ -60,17 +75,180 @@ static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
6075
return ret < 0 ? ret : 1;
6176
}
6277

78+
#ifdef CONFIG_HWMON
79+
static umode_t mv3310_hwmon_is_visible(const void *data,
80+
enum hwmon_sensor_types type,
81+
u32 attr, int channel)
82+
{
83+
if (type == hwmon_chip && attr == hwmon_chip_update_interval)
84+
return 0444;
85+
if (type == hwmon_temp && attr == hwmon_temp_input)
86+
return 0444;
87+
return 0;
88+
}
89+
90+
static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
91+
u32 attr, int channel, long *value)
92+
{
93+
struct phy_device *phydev = dev_get_drvdata(dev);
94+
int temp;
95+
96+
if (type == hwmon_chip && attr == hwmon_chip_update_interval) {
97+
*value = MSEC_PER_SEC;
98+
return 0;
99+
}
100+
101+
if (type == hwmon_temp && attr == hwmon_temp_input) {
102+
temp = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP);
103+
if (temp < 0)
104+
return temp;
105+
106+
*value = ((temp & 0xff) - 75) * 1000;
107+
108+
return 0;
109+
}
110+
111+
return -EOPNOTSUPP;
112+
}
113+
114+
static const struct hwmon_ops mv3310_hwmon_ops = {
115+
.is_visible = mv3310_hwmon_is_visible,
116+
.read = mv3310_hwmon_read,
117+
};
118+
119+
static u32 mv3310_hwmon_chip_config[] = {
120+
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
121+
0,
122+
};
123+
124+
static const struct hwmon_channel_info mv3310_hwmon_chip = {
125+
.type = hwmon_chip,
126+
.config = mv3310_hwmon_chip_config,
127+
};
128+
129+
static u32 mv3310_hwmon_temp_config[] = {
130+
HWMON_T_INPUT,
131+
0,
132+
};
133+
134+
static const struct hwmon_channel_info mv3310_hwmon_temp = {
135+
.type = hwmon_temp,
136+
.config = mv3310_hwmon_temp_config,
137+
};
138+
139+
static const struct hwmon_channel_info *mv3310_hwmon_info[] = {
140+
&mv3310_hwmon_chip,
141+
&mv3310_hwmon_temp,
142+
NULL,
143+
};
144+
145+
static const struct hwmon_chip_info mv3310_hwmon_chip_info = {
146+
.ops = &mv3310_hwmon_ops,
147+
.info = mv3310_hwmon_info,
148+
};
149+
150+
static int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
151+
{
152+
u16 val;
153+
int ret;
154+
155+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP,
156+
MV_V2_TEMP_UNKNOWN);
157+
if (ret < 0)
158+
return ret;
159+
160+
val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE;
161+
ret = mv3310_modify(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL,
162+
MV_V2_TEMP_CTRL_MASK, val);
163+
164+
return ret < 0 ? ret : 0;
165+
}
166+
167+
static void mv3310_hwmon_disable(void *data)
168+
{
169+
struct phy_device *phydev = data;
170+
171+
mv3310_hwmon_config(phydev, false);
172+
}
173+
174+
static int mv3310_hwmon_probe(struct phy_device *phydev)
175+
{
176+
struct device *dev = &phydev->mdio.dev;
177+
struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
178+
int i, j, ret;
179+
180+
priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
181+
if (!priv->hwmon_name)
182+
return -ENODEV;
183+
184+
for (i = j = 0; priv->hwmon_name[i]; i++) {
185+
if (isalnum(priv->hwmon_name[i])) {
186+
if (i != j)
187+
priv->hwmon_name[j] = priv->hwmon_name[i];
188+
j++;
189+
}
190+
}
191+
priv->hwmon_name[j] = '\0';
192+
193+
ret = mv3310_hwmon_config(phydev, true);
194+
if (ret)
195+
return ret;
196+
197+
ret = devm_add_action_or_reset(dev, mv3310_hwmon_disable, phydev);
198+
if (ret)
199+
return ret;
200+
201+
priv->hwmon_dev = devm_hwmon_device_register_with_info(dev,
202+
priv->hwmon_name, phydev,
203+
&mv3310_hwmon_chip_info, NULL);
204+
205+
return PTR_ERR_OR_ZERO(priv->hwmon_dev);
206+
}
207+
#else
208+
static inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
209+
{
210+
return 0;
211+
}
212+
213+
static int mv3310_hwmon_probe(struct phy_device *phydev)
214+
{
215+
return 0;
216+
}
217+
#endif
218+
63219
static int mv3310_probe(struct phy_device *phydev)
64220
{
221+
struct mv3310_priv *priv;
65222
u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
223+
int ret;
66224

67225
if (!phydev->is_c45 ||
68226
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
69227
return -ENODEV;
70228

229+
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
230+
if (!priv)
231+
return -ENOMEM;
232+
233+
dev_set_drvdata(&phydev->mdio.dev, priv);
234+
235+
ret = mv3310_hwmon_probe(phydev);
236+
if (ret)
237+
return ret;
238+
239+
return 0;
240+
}
241+
242+
static int mv3310_suspend(struct phy_device *phydev)
243+
{
71244
return 0;
72245
}
73246

247+
static int mv3310_resume(struct phy_device *phydev)
248+
{
249+
return mv3310_hwmon_config(phydev, true);
250+
}
251+
74252
static int mv3310_config_init(struct phy_device *phydev)
75253
{
76254
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
@@ -367,9 +545,11 @@ static struct phy_driver mv3310_drivers[] = {
367545
SUPPORTED_FIBRE |
368546
SUPPORTED_10000baseT_Full |
369547
SUPPORTED_Backplane,
370-
.probe = mv3310_probe,
371548
.soft_reset = gen10g_no_soft_reset,
372549
.config_init = mv3310_config_init,
550+
.probe = mv3310_probe,
551+
.suspend = mv3310_suspend,
552+
.resume = mv3310_resume,
373553
.config_aneg = mv3310_config_aneg,
374554
.aneg_done = mv3310_aneg_done,
375555
.read_status = mv3310_read_status,

0 commit comments

Comments
 (0)