Commit 9a549c1e authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-hwmon'

Andrew Lunn says:

====================
net: dsa: Move temperature sensor code into PHY.

Marvell Ethernet switches contain a temperature sensor. There appears
to be one sensor, which is shared by each of the internal PHYs. Each
PHY has independent registers to read this sensor, and to set a limit
for when an alarm should be raised.

Some Marvell discrete PHY also have the same sensor and registers.
Moving the HWMON code from DSA into the PHY makes the sensor available
in discrete PHYs, and removes the layering violation, the switch
driver poking around in PHY registers.

While moving the code into the PHY driver, it has been re-written to
use the new HWMON APIs.

v2:

Better Cover note explaining one sensor, but multiple independent
registers

Simply error checking.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 319554f2 cf1a56a4
...@@ -2964,154 +2964,6 @@ static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip) ...@@ -2964,154 +2964,6 @@ static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
of_node_put(chip->mdio_np); of_node_put(chip->mdio_np);
} }
#ifdef CONFIG_NET_DSA_HWMON
static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_chip *chip = ds->priv;
u16 val;
int ret;
*temp = 0;
mutex_lock(&chip->reg_lock);
ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
if (ret < 0)
goto error;
/* Enable temperature sensor */
ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
if (ret < 0)
goto error;
ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
if (ret < 0)
goto error;
/* Wait for temperature to stabilize */
usleep_range(10000, 12000);
ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
if (ret < 0)
goto error;
/* Disable temperature sensor */
ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
if (ret < 0)
goto error;
*temp = ((val & 0x1f) - 5) * 5;
error:
mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
mutex_unlock(&chip->reg_lock);
return ret;
}
static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_chip *chip = ds->priv;
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
u16 val;
int ret;
*temp = 0;
mutex_lock(&chip->reg_lock);
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
mutex_unlock(&chip->reg_lock);
if (ret < 0)
return ret;
*temp = (val & 0xff) - 25;
return 0;
}
static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_chip *chip = ds->priv;
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
return -EOPNOTSUPP;
if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
return mv88e63xx_get_temp(ds, temp);
return mv88e61xx_get_temp(ds, temp);
}
static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_chip *chip = ds->priv;
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
u16 val;
int ret;
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
*temp = 0;
mutex_lock(&chip->reg_lock);
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
mutex_unlock(&chip->reg_lock);
if (ret < 0)
return ret;
*temp = (((val >> 8) & 0x1f) * 5) - 25;
return 0;
}
static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
{
struct mv88e6xxx_chip *chip = ds->priv;
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
u16 val;
int err;
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
if (err)
goto unlock;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
(val & 0xe0ff) | (temp << 8));
unlock:
mutex_unlock(&chip->reg_lock);
return err;
}
static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
{
struct mv88e6xxx_chip *chip = ds->priv;
int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
u16 val;
int ret;
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
*alarm = false;
mutex_lock(&chip->reg_lock);
ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
mutex_unlock(&chip->reg_lock);
if (ret < 0)
return ret;
*alarm = !!(val & 0x40);
return 0;
}
#endif /* CONFIG_NET_DSA_HWMON */
static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
...@@ -4386,12 +4238,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { ...@@ -4386,12 +4238,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_sset_count = mv88e6xxx_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
.set_eee = mv88e6xxx_set_eee, .set_eee = mv88e6xxx_set_eee,
.get_eee = mv88e6xxx_get_eee, .get_eee = mv88e6xxx_get_eee,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp,
.get_temp_limit = mv88e6xxx_get_temp_limit,
.set_temp_limit = mv88e6xxx_set_temp_limit,
.get_temp_alarm = mv88e6xxx_get_temp_alarm,
#endif
.get_eeprom_len = mv88e6xxx_get_eeprom_len, .get_eeprom_len = mv88e6xxx_get_eeprom_len,
.get_eeprom = mv88e6xxx_get_eeprom, .get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom, .set_eeprom = mv88e6xxx_set_eeprom,
......
...@@ -497,12 +497,6 @@ enum mv88e6xxx_cap { ...@@ -497,12 +497,6 @@ enum mv88e6xxx_cap {
*/ */
MV88E6XXX_CAP_STU, MV88E6XXX_CAP_STU,
/* Internal temperature sensor.
* Available from any enabled port's PHY register 26, page 6.
*/
MV88E6XXX_CAP_TEMP,
MV88E6XXX_CAP_TEMP_LIMIT,
/* VLAN Table Unit. /* VLAN Table Unit.
* The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP. * The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP.
*/ */
...@@ -533,8 +527,6 @@ enum mv88e6xxx_cap { ...@@ -533,8 +527,6 @@ enum mv88e6xxx_cap {
#define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT)
#define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) #define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU)
#define MV88E6XXX_FLAG_TEMP BIT_ULL(MV88E6XXX_CAP_TEMP)
#define MV88E6XXX_FLAG_TEMP_LIMIT BIT_ULL(MV88E6XXX_CAP_TEMP_LIMIT)
#define MV88E6XXX_FLAG_VTU BIT_ULL(MV88E6XXX_CAP_VTU) #define MV88E6XXX_FLAG_VTU BIT_ULL(MV88E6XXX_CAP_VTU)
/* Ingress Rate Limit unit */ /* Ingress Rate Limit unit */
...@@ -586,7 +578,6 @@ enum mv88e6xxx_cap { ...@@ -586,7 +578,6 @@ enum mv88e6xxx_cap {
MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_G2_POT | \
MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_STU | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAG_VTU | \
MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_IRL | \
MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_MULTI_CHIP | \
...@@ -605,8 +596,6 @@ enum mv88e6xxx_cap { ...@@ -605,8 +596,6 @@ enum mv88e6xxx_cap {
MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \
MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_G2_POT | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_TEMP_LIMIT | \
MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAG_VTU | \
MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_IRL | \
MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_MULTI_CHIP | \
...@@ -621,7 +610,6 @@ enum mv88e6xxx_cap { ...@@ -621,7 +610,6 @@ enum mv88e6xxx_cap {
MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_G2_POT | \
MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_STU | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAG_VTU | \
MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_IRL | \
MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_MULTI_CHIP | \
...@@ -637,8 +625,6 @@ enum mv88e6xxx_cap { ...@@ -637,8 +625,6 @@ enum mv88e6xxx_cap {
MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \
MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_G2_POT | \
MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_STU | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_TEMP_LIMIT | \
MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAG_VTU | \
MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_IRL | \
MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_MULTI_CHIP | \
...@@ -651,8 +637,6 @@ struct mv88e6xxx_ops; ...@@ -651,8 +637,6 @@ struct mv88e6xxx_ops;
(MV88E6XXX_FLAG_EEE | \ (MV88E6XXX_FLAG_EEE | \
MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_GLOBAL2 | \
MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_STU | \
MV88E6XXX_FLAG_TEMP | \
MV88E6XXX_FLAG_TEMP_LIMIT | \
MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAG_VTU | \
MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_IRL | \
MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_MULTI_CHIP | \
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/hwmon.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -90,6 +92,17 @@ ...@@ -90,6 +92,17 @@
#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4)) #define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4))
#define MII_88E1121_MISC_TEST 0x1a
#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00
#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT 8
#define MII_88E1510_MISC_TEST_TEMP_IRQ_EN BIT(7)
#define MII_88E1510_MISC_TEST_TEMP_IRQ BIT(6)
#define MII_88E1121_MISC_TEST_TEMP_SENSOR_EN BIT(5)
#define MII_88E1121_MISC_TEST_TEMP_MASK 0x1f
#define MII_88E1510_TEMP_SENSOR 0x1b
#define MII_88E1510_TEMP_SENSOR_MASK 0xff
#define MII_88E1318S_PHY_MSCR1_REG 16 #define MII_88E1318S_PHY_MSCR1_REG 16
#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
...@@ -172,6 +185,8 @@ static struct marvell_hw_stat marvell_hw_stats[] = { ...@@ -172,6 +185,8 @@ static struct marvell_hw_stat marvell_hw_stats[] = {
struct marvell_priv { struct marvell_priv {
u64 stats[ARRAY_SIZE(marvell_hw_stats)]; u64 stats[ARRAY_SIZE(marvell_hw_stats)];
char *hwmon_name;
struct device *hwmon_dev;
}; };
static int marvell_ack_interrupt(struct phy_device *phydev) static int marvell_ack_interrupt(struct phy_device *phydev)
...@@ -1468,6 +1483,371 @@ static void marvell_get_stats(struct phy_device *phydev, ...@@ -1468,6 +1483,371 @@ static void marvell_get_stats(struct phy_device *phydev,
data[i] = marvell_get_stat(phydev, i); data[i] = marvell_get_stat(phydev, i);
} }
#ifdef CONFIG_HWMON
static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
{
int ret;
int val;
*temp = 0;
mutex_lock(&phydev->lock);
ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
if (ret < 0)
goto error;
/* Enable temperature sensor */
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
ret = phy_write(phydev, MII_88E1121_MISC_TEST,
ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
if (ret < 0)
goto error;
/* Wait for temperature to stabilize */
usleep_range(10000, 12000);
val = phy_read(phydev, MII_88E1121_MISC_TEST);
if (val < 0) {
ret = val;
goto error;
}
/* Disable temperature sensor */
ret = phy_write(phydev, MII_88E1121_MISC_TEST,
ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
if (ret < 0)
goto error;
*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
error:
phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
mutex_unlock(&phydev->lock);
return ret;
}
static int m88e1121_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{
struct phy_device *phydev = dev_get_drvdata(dev);
int err;
switch (attr) {
case hwmon_temp_input:
err = m88e1121_get_temp(phydev, temp);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static umode_t m88e1121_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
return 0444;
default:
return 0;
}
}
static u32 m88e1121_hwmon_chip_config[] = {
HWMON_C_REGISTER_TZ,
0
};
static const struct hwmon_channel_info m88e1121_hwmon_chip = {
.type = hwmon_chip,
.config = m88e1121_hwmon_chip_config,
};
static u32 m88e1121_hwmon_temp_config[] = {
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info m88e1121_hwmon_temp = {
.type = hwmon_temp,
.config = m88e1121_hwmon_temp_config,
};
static const struct hwmon_channel_info *m88e1121_hwmon_info[] = {
&m88e1121_hwmon_chip,
&m88e1121_hwmon_temp,
NULL
};
static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = {
.is_visible = m88e1121_hwmon_is_visible,
.read = m88e1121_hwmon_read,
};
static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
.ops = &m88e1121_hwmon_hwmon_ops,
.info = m88e1121_hwmon_info,
};
static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
{
int ret;
*temp = 0;
mutex_lock(&phydev->lock);
ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
if (ret < 0)
goto error;
ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
if (ret < 0)
goto error;
*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
error:
phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
mutex_unlock(&phydev->lock);
return ret;
}
int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
{
int ret;
*temp = 0;
mutex_lock(&phydev->lock);
ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
if (ret < 0)
goto error;
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
/* convert to mC */
*temp *= 1000;
error:
phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
mutex_unlock(&phydev->lock);
return ret;
}
int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
{
int ret;
mutex_lock(&phydev->lock);
ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
if (ret < 0)
goto error;
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
temp = temp / 1000;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
ret = phy_write(phydev, MII_88E1121_MISC_TEST,
(ret & ~MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) |
(temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
error:
phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
mutex_unlock(&phydev->lock);
return ret;
}
int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
{
int ret;
*alarm = false;
mutex_lock(&phydev->lock);
ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
if (ret < 0)
goto error;
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
error:
phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
mutex_unlock(&phydev->lock);
return ret;
}
static int m88e1510_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{
struct phy_device *phydev = dev_get_drvdata(dev);
int err;
switch (attr) {
case hwmon_temp_input:
err = m88e1510_get_temp(phydev, temp);
break;
case hwmon_temp_crit:
err = m88e1510_get_temp_critical(phydev, temp);
break;
case hwmon_temp_max_alarm:
err = m88e1510_get_temp_alarm(phydev, temp);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static int m88e1510_hwmon_write(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long temp)
{
struct phy_device *phydev = dev_get_drvdata(dev);
int err;
switch (attr) {
case hwmon_temp_crit:
err = m88e1510_set_temp_critical(phydev, temp);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static umode_t m88e1510_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_max_alarm:
return 0444;
case hwmon_temp_crit:
return 0644;
default:
return 0;
}
}
static u32 m88e1510_hwmon_temp_config[] = {
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM,
0
};
static const struct hwmon_channel_info m88e1510_hwmon_temp = {
.type = hwmon_temp,
.config = m88e1510_hwmon_temp_config,
};
static const struct hwmon_channel_info *m88e1510_hwmon_info[] = {
&m88e1121_hwmon_chip,
&m88e1510_hwmon_temp,
NULL
};
static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = {
.is_visible = m88e1510_hwmon_is_visible,
.read = m88e1510_hwmon_read,
.write = m88e1510_hwmon_write,
};
static const struct hwmon_chip_info m88e1510_hwmon_chip_info = {
.ops = &m88e1510_hwmon_hwmon_ops,
.info = m88e1510_hwmon_info,
};
static int marvell_hwmon_name(struct phy_device *phydev)
{
struct marvell_priv *priv = phydev->priv;
struct device *dev = &phydev->mdio.dev;
const char *devname = dev_name(dev);
size_t len = strlen(devname);
int i, j;
priv->hwmon_name = devm_kzalloc(dev, len, GFP_KERNEL);
if (!priv->hwmon_name)
return -ENOMEM;
for (i = j = 0; i < len && devname[i]; i++) {
if (isalnum(devname[i]))
priv->hwmon_name[j++] = devname[i];
}
return 0;
}
static int marvell_hwmon_probe(struct phy_device *phydev,
const struct hwmon_chip_info *chip)
{
struct marvell_priv *priv = phydev->priv;
struct device *dev = &phydev->mdio.dev;
int err;
err = marvell_hwmon_name(phydev);
if (err)
return err;
priv->hwmon_dev = devm_hwmon_device_register_with_info(
dev, priv->hwmon_name, phydev, chip, NULL);
return PTR_ERR_OR_ZERO(priv->hwmon_dev);
}
static int m88e1121_hwmon_probe(struct phy_device *phydev)
{
return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info);
}
static int m88e1510_hwmon_probe(struct phy_device *phydev)
{
return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info);
}
#else
static int m88e1121_hwmon_probe(struct phy_device *phydev)
{
return 0;
}
static int m88e1510_hwmon_probe(struct phy_device *phydev)
{
return 0;
}
#endif
static int marvell_probe(struct phy_device *phydev) static int marvell_probe(struct phy_device *phydev)
{ {
struct marvell_priv *priv; struct marvell_priv *priv;
...@@ -1481,6 +1861,39 @@ static int marvell_probe(struct phy_device *phydev) ...@@ -1481,6 +1861,39 @@ static int marvell_probe(struct phy_device *phydev)
return 0; return 0;
} }
static int m88e1121_probe(struct phy_device *phydev)
{
int err;
err = marvell_probe(phydev);
if (err)
return err;
return m88e1121_hwmon_probe(phydev);
}
static int m88e1510_probe(struct phy_device *phydev)
{
int err;
err = marvell_probe(phydev);
if (err)
return err;
return m88e1510_hwmon_probe(phydev);
}
static void marvell_remove(struct phy_device *phydev)
{
#ifdef CONFIG_HWMON
struct marvell_priv *priv = phydev->priv;
if (priv && priv->hwmon_dev)
hwmon_device_unregister(priv->hwmon_dev);
#endif
}
static struct phy_driver marvell_drivers[] = { static struct phy_driver marvell_drivers[] = {
{ {
.phy_id = MARVELL_PHY_ID_88E1101, .phy_id = MARVELL_PHY_ID_88E1101,
...@@ -1558,9 +1971,10 @@ static struct phy_driver marvell_drivers[] = { ...@@ -1558,9 +1971,10 @@ static struct phy_driver marvell_drivers[] = {
.phy_id = MARVELL_PHY_ID_88E1121R, .phy_id = MARVELL_PHY_ID_88E1121R,
.phy_id_mask = MARVELL_PHY_ID_MASK, .phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1121R", .name = "Marvell 88E1121R",
.probe = &m88e1121_probe,
.remove = &marvell_remove,
.features = PHY_GBIT_FEATURES, .features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT, .flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
.config_init = &m88e1121_config_init, .config_init = &m88e1121_config_init,
.config_aneg = &m88e1121_config_aneg, .config_aneg = &m88e1121_config_aneg,
.read_status = &marvell_read_status, .read_status = &marvell_read_status,
...@@ -1671,8 +2085,10 @@ static struct phy_driver marvell_drivers[] = { ...@@ -1671,8 +2085,10 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK, .phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1510", .name = "Marvell 88E1510",
.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE, .features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
.probe = &m88e1510_probe,
.remove = &marvell_remove,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT, .flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
.config_init = &m88e1510_config_init, .config_init = &m88e1510_config_init,
.config_aneg = &m88e1510_config_aneg, .config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status, .read_status = &marvell_read_status,
...@@ -1689,9 +2105,10 @@ static struct phy_driver marvell_drivers[] = { ...@@ -1689,9 +2105,10 @@ static struct phy_driver marvell_drivers[] = {
.phy_id = MARVELL_PHY_ID_88E1540, .phy_id = MARVELL_PHY_ID_88E1540,
.phy_id_mask = MARVELL_PHY_ID_MASK, .phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1540", .name = "Marvell 88E1540",
.probe = m88e1510_probe,
.remove = &marvell_remove,
.features = PHY_GBIT_FEATURES, .features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT, .flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
.config_init = &marvell_config_init, .config_init = &marvell_config_init,
.config_aneg = &m88e1510_config_aneg, .config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status, .read_status = &marvell_read_status,
......
...@@ -307,14 +307,6 @@ struct dsa_switch_ops { ...@@ -307,14 +307,6 @@ struct dsa_switch_ops {
int (*get_eee)(struct dsa_switch *ds, int port, int (*get_eee)(struct dsa_switch *ds, int port,
struct ethtool_eee *e); struct ethtool_eee *e);
#ifdef CONFIG_NET_DSA_HWMON
/* Hardware monitoring */
int (*get_temp)(struct dsa_switch *ds, int *temp);
int (*get_temp_limit)(struct dsa_switch *ds, int *temp);
int (*set_temp_limit)(struct dsa_switch *ds, int temp);
int (*get_temp_alarm)(struct dsa_switch *ds, bool *alarm);
#endif
/* EEPROM access */ /* EEPROM access */
int (*get_eeprom_len)(struct dsa_switch *ds); int (*get_eeprom_len)(struct dsa_switch *ds);
int (*get_eeprom)(struct dsa_switch *ds, int (*get_eeprom)(struct dsa_switch *ds,
......
...@@ -15,17 +15,6 @@ config NET_DSA ...@@ -15,17 +15,6 @@ config NET_DSA
if NET_DSA if NET_DSA
config NET_DSA_HWMON
bool "Distributed Switch Architecture HWMON support"
default y
depends on HWMON && !(NET_DSA=y && HWMON=m)
---help---
Say Y if you want to expose thermal sensor data on switches supported
by the Distributed Switch Architecture.
Some of those switches contain thermal sensors. This data is available
via the hwmon sysfs interface and exposes the onboard sensors.
# tagging formats # tagging formats
config NET_DSA_TAG_BRCM config NET_DSA_TAG_BRCM
bool bool
......
# the core # the core
obj-$(CONFIG_NET_DSA) += dsa_core.o obj-$(CONFIG_NET_DSA) += dsa_core.o
dsa_core-y += dsa.o slave.o dsa2.o dsa_core-y += dsa.o slave.o dsa2.o
dsa_core-$(CONFIG_NET_DSA_HWMON) += hwmon.o
# tagging formats # tagging formats
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
......
...@@ -316,8 +316,6 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ...@@ -316,8 +316,6 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
if (ret) if (ret)
return ret; return ret;
dsa_hwmon_register(ds);
return 0; return 0;
} }
...@@ -376,8 +374,6 @@ static void dsa_switch_destroy(struct dsa_switch *ds) ...@@ -376,8 +374,6 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
{ {
int port; int port;
dsa_hwmon_unregister(ds);
/* Destroy network devices for physical switch ports. */ /* Destroy network devices for physical switch ports. */
for (port = 0; port < DSA_MAX_PORTS; port++) { for (port = 0; port < DSA_MAX_PORTS; port++) {
if (!(ds->enabled_port_mask & (1 << port))) if (!(ds->enabled_port_mask & (1 << port)))
......
...@@ -56,15 +56,6 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol); ...@@ -56,15 +56,6 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
int dsa_cpu_port_ethtool_setup(struct dsa_switch *ds); int dsa_cpu_port_ethtool_setup(struct dsa_switch *ds);
void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds); void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds);
/* hwmon.c */
#ifdef CONFIG_NET_DSA_HWMON
void dsa_hwmon_register(struct dsa_switch *ds);
void dsa_hwmon_unregister(struct dsa_switch *ds);
#else
static inline void dsa_hwmon_register(struct dsa_switch *ds) { }
static inline void dsa_hwmon_unregister(struct dsa_switch *ds) { }
#endif
/* slave.c */ /* slave.c */
extern const struct dsa_device_ops notag_netdev_ops; extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds); void dsa_slave_mii_bus_init(struct dsa_switch *ds);
......
/*
* net/dsa/hwmon.c - HWMON subsystem support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/ctype.h>
#include <linux/hwmon.h>
#include <net/dsa.h>
#include "dsa_priv.h"
static ssize_t temp1_input_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dsa_switch *ds = dev_get_drvdata(dev);
int temp, ret;
ret = ds->ops->get_temp(ds, &temp);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", temp * 1000);
}
static DEVICE_ATTR_RO(temp1_input);
static ssize_t temp1_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dsa_switch *ds = dev_get_drvdata(dev);
int temp, ret;
ret = ds->ops->get_temp_limit(ds, &temp);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", temp * 1000);
}
static ssize_t temp1_max_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct dsa_switch *ds = dev_get_drvdata(dev);
int temp, ret;
ret = kstrtoint(buf, 0, &temp);
if (ret < 0)
return ret;
ret = ds->ops->set_temp_limit(ds, DIV_ROUND_CLOSEST(temp, 1000));
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR_RW(temp1_max);
static ssize_t temp1_max_alarm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dsa_switch *ds = dev_get_drvdata(dev);
bool alarm;
int ret;
ret = ds->ops->get_temp_alarm(ds, &alarm);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", alarm);
}
static DEVICE_ATTR_RO(temp1_max_alarm);
static struct attribute *dsa_hwmon_attrs[] = {
&dev_attr_temp1_input.attr, /* 0 */
&dev_attr_temp1_max.attr, /* 1 */
&dev_attr_temp1_max_alarm.attr, /* 2 */
NULL
};
static umode_t dsa_hwmon_attrs_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct dsa_switch *ds = dev_get_drvdata(dev);
const struct dsa_switch_ops *ops = ds->ops;
umode_t mode = attr->mode;
if (index == 1) {
if (!ops->get_temp_limit)
mode = 0;
else if (!ops->set_temp_limit)
mode &= ~S_IWUSR;
} else if (index == 2 && !ops->get_temp_alarm) {
mode = 0;
}
return mode;
}
static const struct attribute_group dsa_hwmon_group = {
.attrs = dsa_hwmon_attrs,
.is_visible = dsa_hwmon_attrs_visible,
};
__ATTRIBUTE_GROUPS(dsa_hwmon);
void dsa_hwmon_register(struct dsa_switch *ds)
{
const char *netname = netdev_name(ds->dst->master_netdev);
char hname[IFNAMSIZ + 1];
int i, j;
/* If the switch provides temperature accessors, register with hardware
* monitoring subsystem. Treat registration error as non-fatal.
*/
if (!ds->ops->get_temp)
return;
/* Create valid hwmon 'name' attribute */
for (i = j = 0; i < IFNAMSIZ && netname[i]; i++) {
if (isalnum(netname[i]))
hname[j++] = netname[i];
}
hname[j] = '\0';
scnprintf(ds->hwmon_name, sizeof(ds->hwmon_name), "%s_dsa%d", hname,
ds->index);
ds->hwmon_dev = hwmon_device_register_with_groups(NULL, ds->hwmon_name,
ds, dsa_hwmon_groups);
if (IS_ERR(ds->hwmon_dev)) {
pr_warn("DSA: failed to register HWMON subsystem for switch %d\n",
ds->index);
ds->hwmon_dev = NULL;
} else {
pr_info("DSA: registered HWMON subsystem for switch %d\n",
ds->index);
}
}
void dsa_hwmon_unregister(struct dsa_switch *ds)
{
if (ds->hwmon_dev) {
hwmon_device_unregister(ds->hwmon_dev);
ds->hwmon_dev = NULL;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment