diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r-- | drivers/net/phy/phy_device.c | 133 |
1 files changed, 89 insertions, 44 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index f761288ab..0bfbabad4 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -205,6 +205,37 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, } EXPORT_SYMBOL(phy_device_create); +/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @dev_addr: MMD address in the PHY. + * @devices_in_package: where to store the devices in package information. + * + * Description: reads devices in package registers of a MMD at @dev_addr + * from PHY at @addr on @bus. + * + * Returns: 0 on success, -EIO on failure. + */ +static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, + u32 *devices_in_package) +{ + int phy_reg, reg_addr; + + reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + *devices_in_package = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + *devices_in_package |= (phy_reg & 0xffff); + + return 0; +} + /** * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. * @bus: the target MII bus @@ -223,38 +254,31 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, int phy_reg; int i, reg_addr; const int num_ids = ARRAY_SIZE(c45_ids->device_ids); + u32 *devs = &c45_ids->devices_in_package; - /* Find first non-zero Devices In package. Device - * zero is reserved, so don't probe it. + /* Find first non-zero Devices In package. Device zero is reserved + * for 802.3 c45 complied PHYs, so don't probe it at first. */ - for (i = 1; - i < num_ids && c45_ids->devices_in_package == 0; - i++) { -retry: reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; - phy_reg = mdiobus_read(bus, addr, reg_addr); + for (i = 1; i < num_ids && *devs == 0; i++) { + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs); if (phy_reg < 0) return -EIO; - c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; - reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS1; - phy_reg = mdiobus_read(bus, addr, reg_addr); - if (phy_reg < 0) - return -EIO; - c45_ids->devices_in_package |= (phy_reg & 0xffff); - - if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { - if (i) { - /* If mostly Fs, there is no device there, - * then let's continue to probe more, as some - * 10G PHYs have zero Devices In package, - * e.g. Cortina CS4315/CS4340 PHY. - */ - i = 0; - goto retry; - } else { - /* no device there, let's get out of here */ + if ((*devs & 0x1fffffff) == 0x1fffffff) { + /* If mostly Fs, there is no device there, + * then let's continue to probe more, as some + * 10G PHYs have zero Devices In package, + * e.g. Cortina CS4315/CS4340 PHY. + */ + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); + if (phy_reg < 0) + return -EIO; + /* no device there, let's get out of here */ + if ((*devs & 0x1fffffff) == 0x1fffffff) { *phy_id = 0xffffffff; return 0; + } else { + break; } } } @@ -1239,6 +1263,44 @@ static int gen10g_resume(struct phy_device *phydev) return 0; } +static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) +{ + /* The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults first + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return -ENOTSUPP; + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + + 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; + + phydev->advertising = phydev->supported; + + return 0; +} +EXPORT_SYMBOL(phy_set_max_speed); + static void of_set_phy_supported(struct phy_device *phydev) { struct device_node *node = phydev->dev.of_node; @@ -1250,25 +1312,8 @@ static void of_set_phy_supported(struct phy_device *phydev) if (!node) return; - if (!of_property_read_u32(node, "max-speed", &max_speed)) { - /* The default values for phydev->supported are provided by the PHY - * driver "features" member, we want to reset to sane defaults fist - * before supporting higher speeds. - */ - phydev->supported &= PHY_DEFAULT_FEATURES; - - switch (max_speed) { - default: - return; - - case SPEED_1000: - phydev->supported |= PHY_1000BT_FEATURES; - case SPEED_100: - phydev->supported |= PHY_100BT_FEATURES; - case SPEED_10: - phydev->supported |= PHY_10BT_FEATURES; - } - } + if (!of_property_read_u32(node, "max-speed", &max_speed)) + __set_phy_supported(phydev, max_speed); } /** |