On Tue, Apr 03, 2018 at 10:31:45AM +0100, Russell King wrote: > 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 <and...@lunn.ch> > Signed-off-by: Russell King <rmk+ker...@armlinux.org.uk> > --- > v2: update to apply to net-next > > drivers/net/phy/marvell10g.c | 184 > ++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 182 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c > index 8a0bd98fdec7..db9d66781da6 100644 > --- a/drivers/net/phy/marvell10g.c > +++ b/drivers/net/phy/marvell10g.c > @@ -21,8 +21,10 @@ > * If both the fiber and copper ports are connected, the first to gain > * link takes priority and the other port is completely locked out. > */ > -#include <linux/phy.h> > +#include <linux/ctype.h> > +#include <linux/hwmon.h> > #include <linux/marvell_phy.h> > +#include <linux/phy.h> > > enum { > MV_PCS_BASE_T = 0x0000, > @@ -40,6 +42,19 @@ enum { > */ > MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */ > MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */ > + > + /* Vendor2 MMD registers */ > + MV_V2_TEMP_CTRL = 0xf08a, > + MV_V2_TEMP_CTRL_MASK = 0xc000, > + MV_V2_TEMP_CTRL_SAMPLE = 0x0000, > + MV_V2_TEMP_CTRL_DISABLE = 0xc000, > + MV_V2_TEMP = 0xf08c, > + MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ > +}; > + > +struct mv3310_priv { > + struct device *hwmon_dev; > + char *hwmon_name; > }; > > 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, > return ret < 0 ? ret : 1; > } > > +#ifdef CONFIG_HWMON > +static umode_t mv3310_hwmon_is_visible(const void *data, > + enum hwmon_sensor_types type, > + u32 attr, int channel) > +{ > + if (type == hwmon_chip && attr == hwmon_chip_update_interval) > + return 0444; > + if (type == hwmon_temp && attr == hwmon_temp_input) > + return 0444; > + return 0; > +} > + > +static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types > type, > + u32 attr, int channel, long *value) > +{ > + struct phy_device *phydev = dev_get_drvdata(dev); > + int temp; > + > + if (type == hwmon_chip && attr == hwmon_chip_update_interval) { > + *value = MSEC_PER_SEC;
The update_interval attribute is supposed to be used for setting an update interval in the chip. Having it return a constant doesn't really serve a useful purpose. Guenter > + return 0; > + } > + > + if (type == hwmon_temp && attr == hwmon_temp_input) { > + temp = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP); > + if (temp < 0) > + return temp; > + > + *value = ((temp & 0xff) - 75) * 1000; > + > + return 0; > + } > + > + return -EOPNOTSUPP; > +} > + > +static const struct hwmon_ops mv3310_hwmon_ops = { > + .is_visible = mv3310_hwmon_is_visible, > + .read = mv3310_hwmon_read, > +}; > + > +static u32 mv3310_hwmon_chip_config[] = { > + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, > + 0, > +}; > + > +static const struct hwmon_channel_info mv3310_hwmon_chip = { > + .type = hwmon_chip, > + .config = mv3310_hwmon_chip_config, > +}; > + > +static u32 mv3310_hwmon_temp_config[] = { > + HWMON_T_INPUT, > + 0, > +}; > + > +static const struct hwmon_channel_info mv3310_hwmon_temp = { > + .type = hwmon_temp, > + .config = mv3310_hwmon_temp_config, > +}; > + > +static const struct hwmon_channel_info *mv3310_hwmon_info[] = { > + &mv3310_hwmon_chip, > + &mv3310_hwmon_temp, > + NULL, > +}; > + > +static const struct hwmon_chip_info mv3310_hwmon_chip_info = { > + .ops = &mv3310_hwmon_ops, > + .info = mv3310_hwmon_info, > +}; > + > +static int mv3310_hwmon_config(struct phy_device *phydev, bool enable) > +{ > + u16 val; > + int ret; > + > + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP, > + MV_V2_TEMP_UNKNOWN); > + if (ret < 0) > + return ret; > + > + val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE; > + ret = mv3310_modify(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL, > + MV_V2_TEMP_CTRL_MASK, val); > + > + return ret < 0 ? ret : 0; > +} > + > +static void mv3310_hwmon_disable(void *data) > +{ > + struct phy_device *phydev = data; > + > + mv3310_hwmon_config(phydev, false); > +} > + > +static int mv3310_hwmon_probe(struct phy_device *phydev) > +{ > + struct device *dev = &phydev->mdio.dev; > + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); > + int i, j, ret; > + > + priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); > + if (!priv->hwmon_name) > + return -ENODEV; > + > + for (i = j = 0; priv->hwmon_name[i]; i++) { > + if (isalnum(priv->hwmon_name[i])) { > + if (i != j) > + priv->hwmon_name[j] = priv->hwmon_name[i]; > + j++; > + } > + } > + priv->hwmon_name[j] = '\0'; > + > + ret = mv3310_hwmon_config(phydev, true); > + if (ret) > + return ret; > + > + ret = devm_add_action_or_reset(dev, mv3310_hwmon_disable, phydev); > + if (ret) > + return ret; > + > + priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, > + priv->hwmon_name, phydev, > + &mv3310_hwmon_chip_info, NULL); > + > + return PTR_ERR_OR_ZERO(priv->hwmon_dev); > +} > +#else > +static inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable) > +{ > + return 0; > +} > + > +static int mv3310_hwmon_probe(struct phy_device *phydev) > +{ > + return 0; > +} > +#endif > + > static int mv3310_probe(struct phy_device *phydev) > { > + struct mv3310_priv *priv; > u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; > + int ret; > > if (!phydev->is_c45 || > (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) > return -ENODEV; > > + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + dev_set_drvdata(&phydev->mdio.dev, priv); > + > + ret = mv3310_hwmon_probe(phydev); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int mv3310_suspend(struct phy_device *phydev) > +{ > return 0; > } > > +static int mv3310_resume(struct phy_device *phydev) > +{ > + return mv3310_hwmon_config(phydev, true); > +} > + > static int mv3310_config_init(struct phy_device *phydev) > { > __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; > @@ -367,9 +545,11 @@ static struct phy_driver mv3310_drivers[] = { > SUPPORTED_FIBRE | > SUPPORTED_10000baseT_Full | > SUPPORTED_Backplane, > - .probe = mv3310_probe, > .soft_reset = gen10g_no_soft_reset, > .config_init = mv3310_config_init, > + .probe = mv3310_probe, > + .suspend = mv3310_suspend, > + .resume = mv3310_resume, > .config_aneg = mv3310_config_aneg, > .aneg_done = mv3310_aneg_done, > .read_status = mv3310_read_status, > -- > 2.7.4 >