Hi,
This is the result of this discussion:
https://lore.kernel.org/netdev/[email protected]/
The goal here is to get the GYP215 and LAN8814 running on the Microchip
LAN9668 SoC. The LAN9668 suppports one external bus and unfortunately, the
LAN8814 has a bug which makes it impossible to use C45 on that bus.
Fortunately, it was the intention of the GPY215 driver to be used on a C22
bus. But I think this could have never really worked, because the
phy_get_c45_ids() will always do c45 accesses and thus on MDIO bus drivers
which will correctly check for the MII_ADDR_C45 flag and return -EOPNOTSUPP
the function call will fail and thus gpy_probe() will fail. This series
tries to fix that and will lay the foundation to add a workaround for the
LAN8814 bug by forcing an MDIO bus to be c22-only.
At the moment, the probe_capabilities is taken into account to decide if
we have to use C45-over-C22. What is still missing from this series is the
handling of a device tree property to restrict the probe_capabilities to
c22-only.
Since net-next is closed, this is marked as RFC to get some early feedback.
Changes since RFC v1:
- use __phy_mmd_indirect() in mdiobus_probe_mmd_read()
- add new properties has_c45 c45_over_c22 (and remove is_c45)
- drop MDIOBUS_NO_CAP handling, Andrew is preparing a series to
add probe_capabilities to mark all C45 capable MDIO bus drivers
Michael Walle (8):
net: phy: mscc-miim: reject clause 45 register accesses
net: phy: mscc-miim: add probe_capabilities
net: phy: add error checks in __phy_mmd_indirect() and export it
net: phy: add error handling for __phy_{read,write}_mmd
net: phy: support indirect c45 access in get_phy_c45_ids()
net: phy: add support for C45-over-C22 transfers
phy: net: introduce phy_promote_to_c45()
net: phy: mxl-gpy: remove unneeded ops
.../net/ethernet/hisilicon/hns/hns_ethtool.c | 4 +-
drivers/net/mdio/mdio-mscc-miim.c | 7 ++
drivers/net/phy/bcm84881.c | 2 +-
drivers/net/phy/marvell10g.c | 2 +-
drivers/net/phy/mxl-gpy.c | 31 +------
drivers/net/phy/phy-core.c | 47 +++++++---
drivers/net/phy/phy.c | 6 +-
drivers/net/phy/phy_device.c | 87 ++++++++++++++++---
drivers/net/phy/phylink.c | 8 +-
include/linux/phy.h | 12 ++-
10 files changed, 136 insertions(+), 70 deletions(-)
--
2.30.2
The driver doesn't support clause 45 register access yet, but doesn't
check if the access is a c45 one either. This leads to spurious register
reads and writes. Add the check.
Fixes: 542671fe4d86 ("net: phy: mscc-miim: Add MDIO driver")
Signed-off-by: Michael Walle <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
---
drivers/net/mdio/mdio-mscc-miim.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c
index c483ba67c21f..582969751b4c 100644
--- a/drivers/net/mdio/mdio-mscc-miim.c
+++ b/drivers/net/mdio/mdio-mscc-miim.c
@@ -102,6 +102,9 @@ static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
u32 val;
int ret;
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
ret = mscc_miim_wait_pending(bus);
if (ret)
goto out;
@@ -145,6 +148,9 @@ static int mscc_miim_write(struct mii_bus *bus, int mii_id,
struct mscc_miim_dev *miim = bus->priv;
int ret;
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
ret = mscc_miim_wait_pending(bus);
if (ret < 0)
goto out;
--
2.30.2
Add missing error checks and export it so it can be reused. Rename the
function to have the common "phy_" prefix.
Signed-off-by: Michael Walle <[email protected]>
---
drivers/net/phy/phy-core.c | 34 ++++++++++++++++++++++++++--------
include/linux/phy.h | 2 ++
2 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 2001f3329133..dd9b6b64757d 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -437,19 +437,37 @@ int phy_speed_down_core(struct phy_device *phydev)
return 0;
}
-static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
- u16 regnum)
+/**
+ * __phy_mmd_indirect - prepare an indirect C45 register access
+ *
+ * @bus: the target MII bus
+ * @phy_addr: PHY address on the MII bus
+ * @devad: The target MMD (0..31)
+ * @regnum: The target register on the MMD (0..65535)
+ *
+ * Prepare an indirect C45 read or write transfer using the MII_MMD_CTRL and
+ * MII_MMD_DATA registers in C22 space.
+ */
+int __phy_mmd_indirect(struct mii_bus *bus, int phy_addr, int devad,
+ u16 regnum)
{
+ int ret;
+
/* Write the desired MMD Devad */
- __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
+ ret = __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
+ if (ret)
+ return ret;
/* Write the desired MMD register address */
- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
+ ret = __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
+ if (ret)
+ return ret;
/* Select the Function : DATA with no post increment */
- __mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
- devad | MII_MMD_CTRL_NOINCR);
+ return __mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
+ devad | MII_MMD_CTRL_NOINCR);
}
+EXPORT_SYMBOL(__phy_mmd_indirect);
/**
* __phy_read_mmd - Convenience function for reading a register
@@ -476,7 +494,7 @@ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
struct mii_bus *bus = phydev->mdio.bus;
int phy_addr = phydev->mdio.addr;
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
+ __phy_mmd_indirect(bus, phy_addr, devad, regnum);
/* Read the content of the MMD's selected register */
val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
@@ -532,7 +550,7 @@ 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;
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
+ __phy_mmd_indirect(bus, phy_addr, devad, regnum);
/* Write the data into MMD's selected register */
__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 36ca2b5c2253..c81c209d4abd 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1110,6 +1110,8 @@ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
__ret; \
})
+int __phy_mmd_indirect(struct mii_bus *bus, int phy_addr, int devad,
+ u16 regnum);
/*
* __phy_read_mmd - Convenience function for reading a register
* from an MMD on a given PHY.
--
2.30.2
The driver is currently only capable of doing c22 accesses. Add the
corresponding probe_capabilities.
Signed-off-by: Michael Walle <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
---
drivers/net/mdio/mdio-mscc-miim.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c
index 582969751b4c..c9efcfa2a1ce 100644
--- a/drivers/net/mdio/mdio-mscc-miim.c
+++ b/drivers/net/mdio/mdio-mscc-miim.c
@@ -225,6 +225,7 @@ int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
bus->read = mscc_miim_read;
bus->write = mscc_miim_write;
bus->reset = mscc_miim_reset;
+ bus->probe_capabilities = MDIOBUS_C22;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
bus->parent = dev;
--
2.30.2
Now that __phy_mmd_indirect() returns an error code, check it and
additionally check the error code of the last read or write access.
Signed-off-by: Michael Walle <[email protected]>
---
drivers/net/phy/phy-core.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index dd9b6b64757d..2a300c58d1e5 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -493,8 +493,11 @@ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
} else {
struct mii_bus *bus = phydev->mdio.bus;
int phy_addr = phydev->mdio.addr;
+ int ret;
- __phy_mmd_indirect(bus, phy_addr, devad, regnum);
+ ret = __phy_mmd_indirect(bus, phy_addr, devad, regnum);
+ if (ret)
+ return ret;
/* Read the content of the MMD's selected register */
val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
@@ -550,12 +553,12 @@ 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;
- __phy_mmd_indirect(bus, phy_addr, devad, regnum);
+ ret = __phy_mmd_indirect(bus, phy_addr, devad, regnum);
+ if (ret)
+ return ret;
/* Write the data into MMD's selected register */
- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
-
- ret = 0;
+ ret = __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
}
return ret;
}
--
2.30.2
Now that we have proper c45-over-c22 support and the PHY driver promote
the PHY to a C45 device, we can drop the ops because the core will
already call them.
Signed-off-by: Michael Walle <[email protected]>
---
drivers/net/phy/mxl-gpy.c | 24 ------------------------
1 file changed, 24 deletions(-)
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index fe9417528444..c68c7621c3d9 100644
--- a/drivers/net/phy/mxl-gpy.c
+++ b/drivers/net/phy/mxl-gpy.c
@@ -513,13 +513,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
.name = "Maxlinear Ethernet GPY2xx",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -531,13 +529,11 @@ static struct phy_driver gpy_drivers[] = {
.phy_id = PHY_ID_GPY115B,
.phy_id_mask = PHY_ID_GPYx15B_MASK,
.name = "Maxlinear Ethernet GPY115B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -548,13 +544,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
.name = "Maxlinear Ethernet GPY115C",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -566,13 +560,11 @@ static struct phy_driver gpy_drivers[] = {
.phy_id = PHY_ID_GPY211B,
.phy_id_mask = PHY_ID_GPY21xB_MASK,
.name = "Maxlinear Ethernet GPY211B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -583,13 +575,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
.name = "Maxlinear Ethernet GPY211C",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -601,13 +591,11 @@ static struct phy_driver gpy_drivers[] = {
.phy_id = PHY_ID_GPY212B,
.phy_id_mask = PHY_ID_GPY21xB_MASK,
.name = "Maxlinear Ethernet GPY212B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -618,13 +606,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
.name = "Maxlinear Ethernet GPY212C",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -636,13 +622,11 @@ static struct phy_driver gpy_drivers[] = {
.phy_id = PHY_ID_GPY215B,
.phy_id_mask = PHY_ID_GPYx15B_MASK,
.name = "Maxlinear Ethernet GPY215B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -653,13 +637,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
.name = "Maxlinear Ethernet GPY215C",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -670,13 +652,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
.name = "Maxlinear Ethernet GPY241B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -687,13 +667,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
.name = "Maxlinear Ethernet GPY241BM",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
@@ -704,13 +682,11 @@ static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
.name = "Maxlinear Ethernet GPY245B",
- .get_features = genphy_c45_pma_read_abilities,
.config_init = gpy_config_init,
.probe = gpy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
.config_aneg = gpy_config_aneg,
- .aneg_done = genphy_c45_aneg_done,
.read_status = gpy_read_status,
.config_intr = gpy_config_intr,
.handle_interrupt = gpy_handle_interrupt,
--
2.30.2
There are some PHYs, namely the Maxlinear GPY215, whose driver is
explicitly supporting C45-over-C22 access. At least that was the
intention. In practice, it cannot work because get_phy_c45_ids()
will always issue c45 register accesses.
There is another issue at hand: the Microchip LAN8814, which is
a c22 only quad PHY, has issues with c45 accesses on the same bus
and its address decoder will find a match in the middle of another
c45 transaction. This will lead to spurious reads and writes. The
reads will corrupt the c45 in flight. The write will lead to random
writes to the LAN8814 registers. As a workaround for PHYs which
support C45-over-C22 register accesses, we can make the MDIO bus
c22-only.
For both reasons, extend the register accesses in get_phy_c45_ids()
to allow indirect accesses, indicated by the bus->probe_capabilities
bits. The probe_capabilities can then be degraded by a device tree
property, for example. Or it will just work when the MDIO driver
is c22-only and set the capabilities accordingly.
Signed-off-by: Michael Walle <[email protected]>
---
drivers/net/phy/phy_device.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8406ac739def..95cd12680e02 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -649,6 +649,28 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
}
EXPORT_SYMBOL(phy_device_create);
+static int mdiobus_probe_mmd_read(struct mii_bus *bus, int prtad, int devad,
+ u16 regnum)
+{
+ int ret;
+
+ if (bus->probe_capabilities >= MDIOBUS_C45)
+ 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
@@ -664,7 +686,7 @@ static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
{
int stat2;
- stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+ stat2 = mdiobus_probe_mmd_read(bus, prtad, devad, MDIO_STAT2);
if (stat2 < 0)
return stat2;
@@ -687,12 +709,12 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
{
int phy_reg;
- phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+ phy_reg = mdiobus_probe_mmd_read(bus, addr, 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 = mdiobus_probe_mmd_read(bus, addr, dev_addr, MDIO_DEVS1);
if (phy_reg < 0)
return -EIO;
*devices_in_package |= phy_reg;
@@ -776,12 +798,12 @@ 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 = mdiobus_probe_mmd_read(bus, addr, 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 = mdiobus_probe_mmd_read(bus, addr, i, MII_PHYSID2);
if (phy_reg < 0)
return -EIO;
c45_ids->device_ids[i] |= phy_reg;
--
2.30.2
If an MDIO bus is only capable of doing C22 transfers we can use
indirect accesses to C45 registers over C22 registers. This was already
the intention of the GPY215 driver. The author described their use case
as follows:
Our product supports both C22 and C45.
In the real system, we found C22 was used by customers (with indirect
access to C45 registers when necessary).
In its probe function phy_get_c45_ids() is called but this will always
do C45 accesses and thus will fail on a C22-only bus. With the current
code we only have the is_c45 property which is used to indicate a C45
but also used to choose the transfer mode. With C45-over-C22 we need to
split these two properties.
Drop the is_c45 and instead introduce two new properties, has_c45 and
c45_over_c22. has_c45 is set to true if this is a C45 PHY and
c45_over_c22 is true if we need to do indirect accesses using the C22
registers.
c45_over_c22 will always be set by just looking at the bus capabilities.
It will be set to true if a bus is C22-only, regardless if the PHY would
support indirect access. Firstly, it is a reasonable assumption that C45
PHYs will support this access and secondly, there is really not much we
can do otherwise.
Signed-off-by: Michael Walle <[email protected]>
---
.../net/ethernet/hisilicon/hns/hns_ethtool.c | 4 ++--
drivers/net/phy/bcm84881.c | 2 +-
drivers/net/phy/marvell10g.c | 2 +-
drivers/net/phy/mxl-gpy.c | 3 ++-
drivers/net/phy/phy-core.c | 4 ++--
drivers/net/phy/phy.c | 6 ++---
drivers/net/phy/phy_device.c | 24 +++++++++++++++----
drivers/net/phy/phylink.c | 8 +++----
include/linux/phy.h | 8 ++++---
9 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index 54faf0f2d1d8..ec683cdc99d7 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -919,7 +919,7 @@ static void hns_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
hns_nic_test_strs[MAC_INTERNALLOOP_MAC]);
ethtool_sprintf(&buff,
hns_nic_test_strs[MAC_INTERNALLOOP_SERDES]);
- if ((netdev->phydev) && (!netdev->phydev->is_c45))
+ if (netdev->phydev && !netdev->phydev->has_c45)
ethtool_sprintf(&buff,
hns_nic_test_strs[MAC_INTERNALLOOP_PHY]);
@@ -979,7 +979,7 @@ static int hns_get_sset_count(struct net_device *netdev, int stringset)
if (priv->ae_handle->phy_if == PHY_INTERFACE_MODE_XGMII)
cnt--;
- if ((!netdev->phydev) || (netdev->phydev->is_c45))
+ if (!netdev->phydev || netdev->phydev->has_c45)
cnt--;
return cnt;
diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index 9717a1626f3f..d9131d5284c1 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -47,7 +47,7 @@ static int bcm84881_probe(struct phy_device *phydev)
/* This driver requires PMAPMD and AN blocks */
const u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
- if (!phydev->is_c45 ||
+ if (!phydev->has_c45 ||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index b6fea119fe13..4e4ede9af159 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -491,7 +491,7 @@ static int mv3310_probe(struct phy_device *phydev)
u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
int ret;
- if (!phydev->is_c45 ||
+ if (!phydev->has_c45 ||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index 5ce1bf03bbd7..2a19905984fc 100644
--- a/drivers/net/phy/mxl-gpy.c
+++ b/drivers/net/phy/mxl-gpy.c
@@ -98,7 +98,8 @@ static int gpy_probe(struct phy_device *phydev)
{
int ret;
- if (!phydev->is_c45) {
+ if (!phydev->has_c45) {
+ phydev->has_c45 = true;
ret = phy_get_c45_ids(phydev);
if (ret < 0)
return ret;
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 2a300c58d1e5..72b8741ca4de 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -487,7 +487,7 @@ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
if (phydev->drv && phydev->drv->read_mmd) {
val = phydev->drv->read_mmd(phydev, devad, regnum);
- } else if (phydev->is_c45) {
+ } else if (phydev->has_c45 && !phydev->c45_over_c22) {
val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
devad, regnum);
} else {
@@ -546,7 +546,7 @@ int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
if (phydev->drv && phydev->drv->write_mmd) {
ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
- } else if (phydev->is_c45) {
+ } else if (phydev->has_c45 && !phydev->c45_over_c22) {
ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
devad, regnum, val);
} else {
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index beb2b66da132..f3adbe9af6d9 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -140,7 +140,7 @@ int phy_restart_aneg(struct phy_device *phydev)
{
int ret;
- if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
+ if (phydev->has_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
ret = genphy_c45_restart_aneg(phydev);
else
ret = genphy_restart_aneg(phydev);
@@ -161,7 +161,7 @@ int phy_aneg_done(struct phy_device *phydev)
{
if (phydev->drv && phydev->drv->aneg_done)
return phydev->drv->aneg_done(phydev);
- else if (phydev->is_c45)
+ else if (phydev->has_c45)
return genphy_c45_aneg_done(phydev);
else
return genphy_aneg_done(phydev);
@@ -657,7 +657,7 @@ int phy_config_aneg(struct phy_device *phydev)
/* Clause 45 PHYs that don't implement Clause 22 registers are not
* allowed to call genphy_config_aneg()
*/
- if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
+ if (phydev->has_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
return genphy_c45_config_aneg(phydev);
return genphy_config_aneg(phydev);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 95cd12680e02..920bc8859069 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -468,7 +468,7 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv)
if (phydrv->match_phy_device)
return phydrv->match_phy_device(phydev);
- if (phydev->is_c45) {
+ if (phydev->has_c45) {
for (i = 1; i < num_ids; i++) {
if (phydev->c45_ids.device_ids[i] == 0xffffffff)
continue;
@@ -599,7 +599,23 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
dev->autoneg = AUTONEG_ENABLE;
- dev->is_c45 = is_c45;
+ /* Depending on the bus capabilities, we have to use C45-over-C22
+ * register access. We have the following cases:
+ *
+ * 1) bus can only do c45.
+ * 2) bus can only do c22.
+ * 3) bus can do c22 and c45.
+ *
+ * 1) and 3) are easy, because we can just use c45 transfers. For 2) we
+ * don't have any other choice but to use c22 transfers. Even if the
+ * PHY wouldn't support it we cannot do any better.
+ *
+ * Set this for C22 PHYs, too, because the PHY driver might promote it
+ * to C45.
+ */
+ dev->c45_over_c22 = bus->probe_capabilities == MDIOBUS_C22;
+
+ dev->has_c45 = is_c45;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
@@ -1406,7 +1422,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
* exist, and we should use the genphy driver.
*/
if (!d->driver) {
- if (phydev->is_c45)
+ if (phydev->has_c45)
d->driver = &genphy_c45_driver.mdiodrv.driver;
else
d->driver = &genphy_driver.mdiodrv.driver;
@@ -3059,7 +3075,7 @@ static int phy_probe(struct device *dev)
linkmode_copy(phydev->supported, phydrv->features);
else if (phydrv->get_features)
err = phydrv->get_features(phydev);
- else if (phydev->is_c45)
+ else if (phydev->has_c45)
err = genphy_c45_pma_read_abilities(phydev);
else
err = genphy_read_abilities(phydev);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 06943889d747..964f74d9eca7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1369,7 +1369,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
* speeds. We really need to know which interface modes the PHY and
* MAC supports to properly work out which linkmodes can be supported.
*/
- if (phy->is_c45 &&
+ if (phy->has_c45 &&
interface != PHY_INTERFACE_MODE_RXAUI &&
interface != PHY_INTERFACE_MODE_XAUI &&
interface != PHY_INTERFACE_MODE_USXGMII)
@@ -2302,7 +2302,7 @@ static int phylink_phy_read(struct phylink *pl, unsigned int phy_id,
prtad = mdio_phy_id_prtad(phy_id);
devad = mdio_phy_id_devad(phy_id);
devad = mdiobus_c45_addr(devad, reg);
- } else if (phydev->is_c45) {
+ } else if (phydev->has_c45 && !phydev->c45_over_c22) {
switch (reg) {
case MII_BMCR:
case MII_BMSR:
@@ -2342,7 +2342,7 @@ static int phylink_phy_write(struct phylink *pl, unsigned int phy_id,
prtad = mdio_phy_id_prtad(phy_id);
devad = mdio_phy_id_devad(phy_id);
devad = mdiobus_c45_addr(devad, reg);
- } else if (phydev->is_c45) {
+ } else if (phydev->has_c45 && !phydev->c45_over_c22) {
switch (reg) {
case MII_BMCR:
case MII_BMSR:
@@ -2717,7 +2717,7 @@ static void phylink_sfp_link_up(void *upstream)
*/
static bool phylink_phy_no_inband(struct phy_device *phy)
{
- return phy->is_c45 &&
+ return phy->has_c45 &&
(phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index c81c209d4abd..64f2dff2f125 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -523,8 +523,9 @@ struct macsec_ops;
* @mdio: MDIO bus this PHY is on
* @drv: Pointer to the driver for this PHY instance
* @phy_id: UID for this device found during discovery
- * @c45_ids: 802.3-c45 Device Identifiers if is_c45.
- * @is_c45: Set to true if this PHY uses clause 45 addressing.
+ * @c45_ids: 802.3-c45 Device Identifiers if has_c45.
+ * @has_c45: Set to true if this PHY has clause 45 address space.
+ * @c45_over_c22: Set to true if c45-over-c22 addressing is used.
* @is_internal: Set to true if this PHY is internal to a MAC.
* @is_pseudo_fixed_link: Set to true if this PHY is an Ethernet switch, etc.
* @is_gigabit_capable: Set to true if PHY supports 1000Mbps
@@ -605,7 +606,8 @@ struct phy_device {
u32 phy_id;
struct phy_c45_device_ids c45_ids;
- unsigned is_c45:1;
+ unsigned has_c45:1;
+ unsigned c45_over_c22:1;
unsigned is_internal:1;
unsigned is_pseudo_fixed_link:1;
unsigned is_gigabit_capable:1;
--
2.30.2
If not explitly asked to be probed as a C45 PHY, on a bus which is
capable of doing both C22 and C45 transfers, C45 PHYs are first tried to
be probed as C22 PHYs. To be able to promote the PHY to be a C45 one,
the driver can call phy_promote_to_c45() in its probe function.
This was already done in the mxl-gpy driver by the following snippet:
if (!phydev->has_c45) {
phydev->has_c45 = true;
ret = phy_get_c45_ids(phydev);
if (ret < 0)
return ret;
}
Move that code into the core by creating a new function
phy_promote_to_c45().
Signed-off-by: Michael Walle <[email protected]>
---
drivers/net/phy/mxl-gpy.c | 8 ++------
drivers/net/phy/phy_device.c | 19 +++++++++++++++----
include/linux/phy.h | 2 +-
3 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index 2a19905984fc..fe9417528444 100644
--- a/drivers/net/phy/mxl-gpy.c
+++ b/drivers/net/phy/mxl-gpy.c
@@ -98,12 +98,8 @@ static int gpy_probe(struct phy_device *phydev)
{
int ret;
- if (!phydev->has_c45) {
- phydev->has_c45 = true;
- ret = phy_get_c45_ids(phydev);
- if (ret < 0)
- return ret;
- }
+ /* This might have been probed as a C22 PHY, but this is a C45 PHY */
+ phy_promote_to_c45(phydev);
/* Show GPY PHY FW version in dmesg */
ret = phy_read(phydev, PHY_FWV);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 920bc8859069..3dc7d012051d 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1007,18 +1007,29 @@ void phy_device_remove(struct phy_device *phydev)
EXPORT_SYMBOL(phy_device_remove);
/**
- * phy_get_c45_ids - Read 802.3-c45 IDs for phy device.
- * @phydev: phy_device structure to read 802.3-c45 IDs
+ * phy_promote_to_c45 - Promote to a C45 PHY
+ * @phydev: phy_device structure
+ *
+ * If a PHY supports both C22 and C45 and it isn't specifically asked to probe
+ * as a C45 PHY it might be probed as a C22 PHY. The driver can call this
+ * function to promote a PHY from C22 to C45.
+ *
+ * Can also be called if a PHY is already a C45 one. In this case this does
+ * nothing.
*
* Returns zero on success, %-EIO on bus access error, or %-ENODEV if
* the "devices in package" is invalid.
*/
-int phy_get_c45_ids(struct phy_device *phydev)
+int phy_promote_to_c45(struct phy_device *phydev)
{
+ if (phydev->has_c45)
+ return 0;
+
+ phydev->has_c45 = true;
return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
&phydev->c45_ids);
}
-EXPORT_SYMBOL(phy_get_c45_ids);
+EXPORT_SYMBOL(phy_promote_to_c45);
/**
* phy_find_first - finds the first PHY device on the bus
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 64f2dff2f125..4473e760264a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1470,7 +1470,7 @@ static inline int phy_device_register(struct phy_device *phy)
static inline void phy_device_free(struct phy_device *phydev) { }
#endif /* CONFIG_PHYLIB */
void phy_device_remove(struct phy_device *phydev);
-int phy_get_c45_ids(struct phy_device *phydev);
+int phy_promote_to_c45(struct phy_device *phydev);
int phy_init_hw(struct phy_device *phydev);
int phy_suspend(struct phy_device *phydev);
int phy_resume(struct phy_device *phydev);
--
2.30.2