net: phy: Mask-out non-compatible modes when setting the max-speed

When setting a PHY's max speed using either the max-speed DT property
or ethtool, we should mask-out all non-compatible modes according to the
settings table, instead of just the 10/100BASET modes.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Suggested-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Maxime Chevallier 2019-02-11 15:25:26 +01:00 committed by David S. Miller
parent d2d37444e5
commit a4eaed9f9a
3 changed files with 46 additions and 53 deletions

View File

@ -4,6 +4,7 @@
*/ */
#include <linux/export.h> #include <linux/export.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/of.h>
const char *phy_speed_to_str(int speed) const char *phy_speed_to_str(int speed)
{ {
@ -338,6 +339,50 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
return count; return count;
} }
static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
{
const struct phy_setting *p;
int i;
for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
if (p->speed > max_speed)
linkmode_clear_bit(p->bit, phydev->supported);
else
break;
}
return 0;
}
int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
{
int err;
err = __set_phy_supported(phydev, max_speed);
if (err)
return err;
linkmode_copy(phydev->advertising, phydev->supported);
return 0;
}
EXPORT_SYMBOL(phy_set_max_speed);
void of_set_phy_supported(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
u32 max_speed;
if (!IS_ENABLED(CONFIG_OF_MDIO))
return;
if (!node)
return;
if (!of_property_read_u32(node, "max-speed", &max_speed))
__set_phy_supported(phydev, max_speed);
}
/** /**
* phy_resolve_aneg_linkmode - resolve the advertisements into phy settings * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings
* @phydev: The phy_device struct * @phydev: The phy_device struct

View File

@ -1949,44 +1949,6 @@ int genphy_loopback(struct phy_device *phydev, bool enable)
} }
EXPORT_SYMBOL(genphy_loopback); EXPORT_SYMBOL(genphy_loopback);
static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
{
switch (max_speed) {
case SPEED_10:
linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
phydev->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
phydev->supported);
/* fall through */
case SPEED_100:
linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->supported);
break;
case SPEED_1000:
break;
default:
return -ENOTSUPP;
}
return 0;
}
int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
{
int err;
err = __set_phy_supported(phydev, max_speed);
if (err)
return err;
linkmode_copy(phydev->advertising, phydev->supported);
return 0;
}
EXPORT_SYMBOL(phy_set_max_speed);
/** /**
* phy_remove_link_mode - Remove a supported link mode * phy_remove_link_mode - Remove a supported link mode
* @phydev: phy_device structure to remove link mode from * @phydev: phy_device structure to remove link mode from
@ -2117,21 +2079,6 @@ bool phy_validate_pause(struct phy_device *phydev,
} }
EXPORT_SYMBOL(phy_validate_pause); EXPORT_SYMBOL(phy_validate_pause);
static void of_set_phy_supported(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
u32 max_speed;
if (!IS_ENABLED(CONFIG_OF_MDIO))
return;
if (!node)
return;
if (!of_property_read_u32(node, "max-speed", &max_speed))
__set_phy_supported(phydev, max_speed);
}
static void of_set_phy_eee_broken(struct phy_device *phydev) static void of_set_phy_eee_broken(struct phy_device *phydev)
{ {
struct device_node *node = phydev->mdio.dev.of_node; struct device_node *node = phydev->mdio.dev.of_node;

View File

@ -673,6 +673,7 @@ phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
bool exact); bool exact);
size_t phy_speeds(unsigned int *speeds, size_t size, size_t phy_speeds(unsigned int *speeds, size_t size,
unsigned long *mask); unsigned long *mask);
void of_set_phy_supported(struct phy_device *phydev);
static inline bool __phy_is_started(struct phy_device *phydev) static inline bool __phy_is_started(struct phy_device *phydev)
{ {