From eda80b249df7bbc7b3dd13907343a3e59bfc57fd Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 4 Jan 2022 12:06:22 +0000 Subject: [PATCH 1/3] net: ethernet: mtk_eth_soc: fix return values and refactor MDIO ops Instead of returning -1 (-EPERM) when MDIO bus is stuck busy while writing or 0xffff if it happens while reading, return the appropriate -ETIMEDOUT. Also fix return type to int instead of u32. Refactor functions to use bitfield helpers instead of having various masking and shifting constants in the code, which also results in the register definitions in the header file being more obviously related to what is stated in the MediaTek's Reference Manual. Fixes: 656e705243fd0 ("net-next: mediatek: add support for MT7623 ethernet") Signed-off-by: Daniel Golle Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 ++++++++++++--------- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 16 +++++-- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index bcb91b01e69f..727650d8b209 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -91,46 +91,53 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth) } dev_err(eth->dev, "mdio: MDIO timeout\n"); - return -1; + return -ETIMEDOUT; } -static u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr, - u32 phy_register, u32 write_data) +static int _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg, + u32 write_data) { - if (mtk_mdio_busy_wait(eth)) - return -1; + int ret; - write_data &= 0xffff; + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; - mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_WRITE | - (phy_register << PHY_IAC_REG_SHIFT) | - (phy_addr << PHY_IAC_ADDR_SHIFT) | write_data, + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C22 | + PHY_IAC_CMD_WRITE | + PHY_IAC_REG(phy_reg) | + PHY_IAC_ADDR(phy_addr) | + PHY_IAC_DATA(write_data), MTK_PHY_IAC); - if (mtk_mdio_busy_wait(eth)) - return -1; + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; return 0; } -static u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg) +static int _mtk_mdio_read(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg) { - u32 d; + int ret; - if (mtk_mdio_busy_wait(eth)) - return 0xffff; + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; - mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_READ | - (phy_reg << PHY_IAC_REG_SHIFT) | - (phy_addr << PHY_IAC_ADDR_SHIFT), + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C22 | + PHY_IAC_CMD_C22_READ | + PHY_IAC_REG(phy_reg) | + PHY_IAC_ADDR(phy_addr), MTK_PHY_IAC); - if (mtk_mdio_busy_wait(eth)) - return 0xffff; + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; - d = mtk_r32(eth, MTK_PHY_IAC) & 0xffff; - - return d; + return mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_DATA_MASK; } static int mtk_mdio_write(struct mii_bus *bus, int phy_addr, diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 5ef70dd8b49c..f2d90639d7ed 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -341,11 +341,17 @@ /* PHY Indirect Access Control registers */ #define MTK_PHY_IAC 0x10004 #define PHY_IAC_ACCESS BIT(31) -#define PHY_IAC_READ BIT(19) -#define PHY_IAC_WRITE BIT(18) -#define PHY_IAC_START BIT(16) -#define PHY_IAC_ADDR_SHIFT 20 -#define PHY_IAC_REG_SHIFT 25 +#define PHY_IAC_REG_MASK GENMASK(29, 25) +#define PHY_IAC_REG(x) FIELD_PREP(PHY_IAC_REG_MASK, (x)) +#define PHY_IAC_ADDR_MASK GENMASK(24, 20) +#define PHY_IAC_ADDR(x) FIELD_PREP(PHY_IAC_ADDR_MASK, (x)) +#define PHY_IAC_CMD_MASK GENMASK(19, 18) +#define PHY_IAC_CMD_WRITE FIELD_PREP(PHY_IAC_CMD_MASK, 1) +#define PHY_IAC_CMD_C22_READ FIELD_PREP(PHY_IAC_CMD_MASK, 2) +#define PHY_IAC_START_MASK GENMASK(17, 16) +#define PHY_IAC_START_C22 FIELD_PREP(PHY_IAC_START_MASK, 1) +#define PHY_IAC_DATA_MASK GENMASK(15, 0) +#define PHY_IAC_DATA(x) FIELD_PREP(PHY_IAC_DATA_MASK, (x)) #define PHY_IAC_TIMEOUT HZ #define MTK_MAC_MISC 0x1000c From c6af53f038aa32cec12e8a305ba07c7ef168f1b0 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 4 Jan 2022 12:07:00 +0000 Subject: [PATCH 2/3] net: mdio: add helpers to extract clause 45 regad and devad fields Add a couple of helpers and definitions to extract the clause 45 regad and devad fields from the regnum passed into MDIO drivers. Tested-by: Daniel Golle Reviewed-by: Andrew Lunn Signed-off-by: Russell King (Oracle) Signed-off-by: Daniel Golle Signed-off-by: David S. Miller --- include/linux/mdio.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 9f3587a61e14..ecac96d52e01 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -7,6 +7,7 @@ #define __LINUX_MDIO_H__ #include +#include #include /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit @@ -14,6 +15,7 @@ */ #define MII_ADDR_C45 (1<<30) #define MII_DEVADDR_C45_SHIFT 16 +#define MII_DEVADDR_C45_MASK GENMASK(20, 16) #define MII_REGADDR_C45_MASK GENMASK(15, 0) struct gpio_desc; @@ -381,6 +383,16 @@ static inline u32 mdiobus_c45_addr(int devad, u16 regnum) return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; } +static inline u16 mdiobus_c45_regad(u32 regnum) +{ + return FIELD_GET(MII_REGADDR_C45_MASK, regnum); +} + +static inline u16 mdiobus_c45_devad(u32 regnum) +{ + return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); +} + static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, u16 regnum) { From e2e7f6e29c99a1c6afc0e0aa4b9ea80302d28720 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 4 Jan 2022 12:07:46 +0000 Subject: [PATCH 3/3] net: ethernet: mtk_eth_soc: implement Clause 45 MDIO access Implement read and write access to IEEE 802.3 Clause 45 Ethernet phy registers while making use of new mdiobus_c45_regad and mdiobus_c45_devad helpers. Tested on the Ubiquiti UniFi 6 LR access point featuring MediaTek MT7622BV WiSoC with Aquantia AQR112C. Signed-off-by: Daniel Golle Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 70 +++++++++++++++++---- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 + 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 727650d8b209..b67b4323cff0 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -103,13 +103,35 @@ static int _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg, if (ret < 0) return ret; - mtk_w32(eth, PHY_IAC_ACCESS | - PHY_IAC_START_C22 | - PHY_IAC_CMD_WRITE | - PHY_IAC_REG(phy_reg) | - PHY_IAC_ADDR(phy_addr) | - PHY_IAC_DATA(write_data), - MTK_PHY_IAC); + if (phy_reg & MII_ADDR_C45) { + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C45 | + PHY_IAC_CMD_C45_ADDR | + PHY_IAC_REG(mdiobus_c45_devad(phy_reg)) | + PHY_IAC_ADDR(phy_addr) | + PHY_IAC_DATA(mdiobus_c45_regad(phy_reg)), + MTK_PHY_IAC); + + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; + + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C45 | + PHY_IAC_CMD_WRITE | + PHY_IAC_REG(mdiobus_c45_devad(phy_reg)) | + PHY_IAC_ADDR(phy_addr) | + PHY_IAC_DATA(write_data), + MTK_PHY_IAC); + } else { + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C22 | + PHY_IAC_CMD_WRITE | + PHY_IAC_REG(phy_reg) | + PHY_IAC_ADDR(phy_addr) | + PHY_IAC_DATA(write_data), + MTK_PHY_IAC); + } ret = mtk_mdio_busy_wait(eth); if (ret < 0) @@ -126,12 +148,33 @@ static int _mtk_mdio_read(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg) if (ret < 0) return ret; - mtk_w32(eth, PHY_IAC_ACCESS | - PHY_IAC_START_C22 | - PHY_IAC_CMD_C22_READ | - PHY_IAC_REG(phy_reg) | - PHY_IAC_ADDR(phy_addr), - MTK_PHY_IAC); + if (phy_reg & MII_ADDR_C45) { + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C45 | + PHY_IAC_CMD_C45_ADDR | + PHY_IAC_REG(mdiobus_c45_devad(phy_reg)) | + PHY_IAC_ADDR(phy_addr) | + PHY_IAC_DATA(mdiobus_c45_regad(phy_reg)), + MTK_PHY_IAC); + + ret = mtk_mdio_busy_wait(eth); + if (ret < 0) + return ret; + + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C45 | + PHY_IAC_CMD_C45_READ | + PHY_IAC_REG(mdiobus_c45_devad(phy_reg)) | + PHY_IAC_ADDR(phy_addr), + MTK_PHY_IAC); + } else { + mtk_w32(eth, PHY_IAC_ACCESS | + PHY_IAC_START_C22 | + PHY_IAC_CMD_C22_READ | + PHY_IAC_REG(phy_reg) | + PHY_IAC_ADDR(phy_addr), + MTK_PHY_IAC); + } ret = mtk_mdio_busy_wait(eth); if (ret < 0) @@ -504,6 +547,7 @@ static int mtk_mdio_init(struct mtk_eth *eth) eth->mii_bus->name = "mdio"; eth->mii_bus->read = mtk_mdio_read; eth->mii_bus->write = mtk_mdio_write; + eth->mii_bus->probe_capabilities = MDIOBUS_C22_C45; eth->mii_bus->priv = eth; eth->mii_bus->parent = eth->dev; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index f2d90639d7ed..c9d42be314b5 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -346,9 +346,12 @@ #define PHY_IAC_ADDR_MASK GENMASK(24, 20) #define PHY_IAC_ADDR(x) FIELD_PREP(PHY_IAC_ADDR_MASK, (x)) #define PHY_IAC_CMD_MASK GENMASK(19, 18) +#define PHY_IAC_CMD_C45_ADDR FIELD_PREP(PHY_IAC_CMD_MASK, 0) #define PHY_IAC_CMD_WRITE FIELD_PREP(PHY_IAC_CMD_MASK, 1) #define PHY_IAC_CMD_C22_READ FIELD_PREP(PHY_IAC_CMD_MASK, 2) +#define PHY_IAC_CMD_C45_READ FIELD_PREP(PHY_IAC_CMD_MASK, 3) #define PHY_IAC_START_MASK GENMASK(17, 16) +#define PHY_IAC_START_C45 FIELD_PREP(PHY_IAC_START_MASK, 0) #define PHY_IAC_START_C22 FIELD_PREP(PHY_IAC_START_MASK, 1) #define PHY_IAC_DATA_MASK GENMASK(15, 0) #define PHY_IAC_DATA(x) FIELD_PREP(PHY_IAC_DATA_MASK, (x))