@@ -575,26 +575,34 @@ EXPORT_SYMBOL(__phy_mmd_indirect);
*/
int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
{
- int val;
+ struct mii_bus *bus = phydev->mdio.bus;
+ int phy_addr = phydev->mdio.addr;
+ bool check_rc = true;
+ int ret;
if (regnum > (u16)~0 || devad > 32)
return -EINVAL;
- if (phydev->drv && phydev->drv->read_mmd) {
- val = phydev->drv->read_mmd(phydev, devad, regnum);
- } else if (phy_is_c45(phydev)) {
- val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
- devad, regnum);
- } else {
- struct mii_bus *bus = phydev->mdio.bus;
- int phy_addr = phydev->mdio.addr;
-
- mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+ if (phydev->drv && phydev->drv->read_mmd)
+ return phydev->drv->read_mmd(phydev, devad, regnum);
+
+ switch (phydev->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 */
+ 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 */
- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
+ return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
}
- return val;
+
+ return -EOPNOTSUPP;
}
EXPORT_SYMBOL(__phy_read_mmd);
@@ -631,28 +639,35 @@ EXPORT_SYMBOL(phy_read_mmd);
*/
int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
{
+ struct mii_bus *bus = phydev->mdio.bus;
+ int phy_addr = phydev->mdio.addr;
+ bool check_rc = true;
int ret;
if (regnum > (u16)~0 || devad > 32)
return -EINVAL;
- if (phydev->drv && phydev->drv->write_mmd) {
- ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
- } else if (phy_is_c45(phydev)) {
- ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
- devad, regnum, val);
- } else {
- struct mii_bus *bus = phydev->mdio.bus;
- int phy_addr = phydev->mdio.addr;
-
- mmd_phy_indirect(bus, phy_addr, devad, regnum, false);
+ if (phydev->drv && phydev->drv->write_mmd)
+ return phydev->drv->write_mmd(phydev, devad, regnum, val);
+
+ switch (phydev->access_mode) {
+ case PHY_ACCESS_C45:
+ return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
+ case PHY_ACCESS_C22:
+ 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);
- ret = 0;
+ return check_rc ? ret : 0;
}
- return ret;
+
+ return -EOPNOTSUPP;
}
EXPORT_SYMBOL(__phy_write_mmd);
@@ -715,6 +715,28 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
}
EXPORT_SYMBOL(phy_device_create);
+static int phy_probe_mmd_read(struct mii_bus *bus, int prtad, int devad,
+ u16 regnum, bool c45_over_c22)
+{
+ int ret;
+
+ if (!c45_over_c22)
+ return mdiobus_c45_read(bus, prtad, devad, regnum);
+
+ mutex_lock(&bus->mdio_lock);
+
+ ret = __phy_mmd_indirect(bus, prtad, devad, regnum);
+ if (ret)
+ goto out;
+
+ ret = __mdiobus_read(bus, prtad, MII_MMD_DATA);
+
+out:
+ mutex_unlock(&bus->mdio_lock);
+
+ return ret;
+}
+
/* phy_c45_probe_present - checks to see if a MMD is present in the package
* @bus: the target MII bus
* @prtad: PHY package address on the MII bus
@@ -726,11 +748,13 @@ 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, int devad,
+ bool c45_over_c22)
{
int stat2;
- stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+ stat2 = phy_probe_mmd_read(bus, prtad, devad, MDIO_STAT2,
+ c45_over_c22);
if (stat2 < 0)
return stat2;
@@ -749,16 +773,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,
- u32 *devices_in_package)
+ u32 *devices_in_package, bool c45_over_c22)
{
int phy_reg;
- phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+ phy_reg = phy_probe_mmd_read(bus, addr, dev_addr, MDIO_DEVS2,
+ c45_over_c22);
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_probe_mmd_read(bus, addr, dev_addr, MDIO_DEVS1,
+ c45_over_c22);
if (phy_reg < 0)
return -EIO;
*devices_in_package |= phy_reg;
@@ -780,7 +806,8 @@ 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,
- struct phy_c45_device_ids *c45_ids)
+ struct phy_c45_device_ids *c45_ids,
+ bool c45_over_c22)
{
const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
u32 devs_in_pkg = 0;
@@ -798,14 +825,16 @@ 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, i,
+ c45_over_c22);
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, i, &devs_in_pkg,
+ c45_over_c22);
if (phy_reg < 0)
return -EIO;
}
@@ -815,7 +844,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, 0, &devs_in_pkg,
+ c45_over_c22);
if (phy_reg < 0)
return -EIO;
@@ -834,7 +864,8 @@ 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, i,
+ c45_over_c22);
if (ret < 0)
return ret;
@@ -842,12 +873,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_probe_mmd_read(bus, addr, i, MII_PHYSID1,
+ c45_over_c22);
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_probe_mmd_read(bus, addr, i, MII_PHYSID2,
+ c45_over_c22);
if (phy_reg < 0)
return -EIO;
c45_ids->device_ids[i] |= phy_reg;
@@ -961,7 +994,10 @@ 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);
+ r = get_phy_c45_ids(bus, addr, &c45_ids, false);
+ break;
+ case PHY_ACCESS_C45_OVER_C22:
+ r = get_phy_c45_ids(bus, addr, &c45_ids, true);
break;
default:
return ERR_PTR(-EIO);
@@ -976,7 +1012,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, &c45_ids, false);
if (!r)
return phy_device_create(bus, addr, phy_id,
PHY_ACCESS_C45, &c45_ids);
@@ -1058,7 +1094,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);
+ &phydev->c45_ids, false);
}
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,
PHY_ACCESS_C45,
+ PHY_ACCESS_C45_OVER_C22,
};
/**