@@ -550,12 +550,20 @@ static int __phy_mdiobus_read_mmd(struct mii_bus *bus, int phy_addr,
enum phy_access_mode access_mode,
int devad, u32 regnum)
{
+ bool check_rc = true;
+ int ret;
+
switch (access_mode) {
case PHY_ACCESS_C45:
return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
case PHY_ACCESS_C22:
/* ignore return value for legacy reasons */
- mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+ check_rc = false;
+ fallthrough;
+ case PHY_ACCESS_C45_OVER_C22:
+ ret = mmd_phy_indirect(bus, phy_addr, devad, regnum, check_rc);
+ if (check_rc && ret)
+ return ret;
/* Read the content of the MMD's selected register */
return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
@@ -597,17 +605,24 @@ static int __phy_mdiobus_write_mmd(struct mii_bus *bus, int phy_addr,
enum phy_access_mode mode,
int devad, u32 regnum, u16 val)
{
+ bool check_rc = true;
+ int ret;
+
switch (mode) {
case PHY_ACCESS_C45:
return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
case PHY_ACCESS_C22:
- /* ignore return value for legacy reasons */
- mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+ check_rc = false;
+ fallthrough;
+ case PHY_ACCESS_C45_OVER_C22:
+ ret = mmd_phy_indirect(bus, phy_addr, devad, regnum, check_rc);
+ if (check_rc && ret)
+ return ret;
/* Write the data into MMD's selected register */
- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
+ ret = __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
- return 0;
+ return check_rc ? ret : 0;
default:
return -EOPNOTSUPP;
}
@@ -726,11 +726,12 @@ EXPORT_SYMBOL(phy_device_create);
* Returns: negative error number on bus access error, zero if no device
* is responding, or positive if a device is present.
*/
-static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
+static int phy_c45_probe_present(struct mii_bus *bus, int prtad,
+ enum phy_access_mode mode, int devad)
{
int stat2;
- stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+ stat2 = phy_mdiobus_read_mmd(bus, prtad, mode, devad, MDIO_STAT2);
if (stat2 < 0)
return stat2;
@@ -748,17 +749,18 @@ static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
*
* Returns: 0 on success, -EIO on failure.
*/
-static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr,
+ enum phy_access_mode mode, int dev_addr,
u32 *devices_in_package)
{
int phy_reg;
- phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+ phy_reg = phy_mdiobus_read_mmd(bus, addr, mode, dev_addr, MDIO_DEVS2);
if (phy_reg < 0)
return -EIO;
*devices_in_package = phy_reg << 16;
- phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1);
+ phy_reg = phy_mdiobus_read_mmd(bus, addr, mode, dev_addr, MDIO_DEVS1);
if (phy_reg < 0)
return -EIO;
*devices_in_package |= phy_reg;
@@ -780,6 +782,7 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
* the "devices in package" is invalid.
*/
static int get_phy_c45_ids(struct mii_bus *bus, int addr,
+ enum phy_access_mode mode,
struct phy_c45_device_ids *c45_ids)
{
const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
@@ -798,14 +801,15 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
* Some PHYs (88x3310) vendor space is not IEEE802.3
* compliant.
*/
- ret = phy_c45_probe_present(bus, addr, i);
+ ret = phy_c45_probe_present(bus, addr, mode, i);
if (ret < 0)
return -EIO;
if (!ret)
continue;
}
- phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, mode, i,
+ &devs_in_pkg);
if (phy_reg < 0)
return -EIO;
}
@@ -815,7 +819,8 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
* MMD 0, 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_in_pkg);
+ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, mode, 0,
+ &devs_in_pkg);
if (phy_reg < 0)
return -EIO;
@@ -834,7 +839,7 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
* to ignore these if they do not contain IEEE 802.3
* registers.
*/
- ret = phy_c45_probe_present(bus, addr, i);
+ ret = phy_c45_probe_present(bus, addr, mode, i);
if (ret < 0)
return ret;
@@ -842,12 +847,14 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
continue;
}
- phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
+ phy_reg = phy_mdiobus_read_mmd(bus, addr, mode, i,
+ MII_PHYSID1);
if (phy_reg < 0)
return -EIO;
c45_ids->device_ids[i] = phy_reg << 16;
- phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
+ phy_reg = phy_mdiobus_read_mmd(bus, addr, mode, i,
+ MII_PHYSID2);
if (phy_reg < 0)
return -EIO;
c45_ids->device_ids[i] |= phy_reg;
@@ -937,6 +944,11 @@ EXPORT_SYMBOL(fwnode_get_phy_id);
* If the "devices in package" appears valid, read the ID registers for each
* MMD, allocate and return a &struct phy_device.
*
+ * When using %PHY_ACCESS_C45_OVER_C22 as @mode care have to be taken to not
+ * access a non-PHY device as C45-over-C22 is a property of a PHY and not a
+ * generic MDIO device. As the access involves register writes, it may be
+ * destructive on non-PHY devices. IOW, it cannot be used for bus scanning.
+ *
* Returns an allocated &struct phy_device on success, %-ENODEV if there is
* no PHY present, or %-EIO on bus access error.
*/
@@ -956,7 +968,8 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr,
r = get_phy_c22_id(bus, addr, &phy_id);
break;
case PHY_ACCESS_C45:
- r = get_phy_c45_ids(bus, addr, &c45_ids);
+ case PHY_ACCESS_C45_OVER_C22:
+ r = get_phy_c45_ids(bus, addr, mode, &c45_ids);
break;
default:
return ERR_PTR(-EIO);
@@ -971,7 +984,7 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr,
* space, if successful, create the C45 PHY device.
*/
if (mode == PHY_ACCESS_C22 && phy_id == 0 && bus->read_c45) {
- r = get_phy_c45_ids(bus, addr, &c45_ids);
+ r = get_phy_c45_ids(bus, addr, PHY_ACCESS_C45, &c45_ids);
if (!r)
return phy_device_create(bus, addr, phy_id,
PHY_ACCESS_C45, &c45_ids);
@@ -1053,7 +1066,7 @@ EXPORT_SYMBOL(phy_device_remove);
int phy_get_c45_ids(struct phy_device *phydev)
{
return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
- &phydev->c45_ids);
+ PHY_ACCESS_C45, &phydev->c45_ids);
}
EXPORT_SYMBOL(phy_get_c45_ids);
@@ -541,10 +541,13 @@ struct macsec_ops;
*
* @PHY_ACCESS_C22: use 802.3 c22 MDIO transactions
* @PHY_ACCESS_C45: use 802.3 c45 MDIO transactions
+ * @PHY_ACCESS_C45_OVER_C22: indirectly access C45 registers by using by 802.3
+ * c22 MDIO transactions and registers 13 and 14.
*/
enum phy_access_mode {
PHY_ACCESS_C22 = 22,
PHY_ACCESS_C45 = 45,
+ PHY_ACCESS_C45_OVER_C22 = 4522,
};
/**