14fa9c49fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27ac6653aSJeff Kirsher /*******************************************************************************
37ac6653aSJeff Kirsher   STMMAC Ethernet Driver -- MDIO bus implementation
47ac6653aSJeff Kirsher   Provides Bus interface for MII registers
57ac6653aSJeff Kirsher 
67ac6653aSJeff Kirsher   Copyright (C) 2007-2009  STMicroelectronics Ltd
77ac6653aSJeff Kirsher 
87ac6653aSJeff Kirsher 
97ac6653aSJeff Kirsher   Author: Carl Shaw <carl.shaw@st.com>
107ac6653aSJeff Kirsher   Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
117ac6653aSJeff Kirsher *******************************************************************************/
127ac6653aSJeff Kirsher 
137c86f20dSMartin Blumenstingl #include <linux/gpio/consumer.h>
14bbf89284SLABBE Corentin #include <linux/io.h>
15a5f48adcSLABBE Corentin #include <linux/iopoll.h>
167ac6653aSJeff Kirsher #include <linux/mii.h>
17e34d6569SPhil Reid #include <linux/of_mdio.h>
185ec55823SJoakim Zhang #include <linux/pm_runtime.h>
19bbf89284SLABBE Corentin #include <linux/phy.h>
2042a90766SMartin Blumenstingl #include <linux/property.h>
21bbf89284SLABBE Corentin #include <linux/slab.h>
227ac6653aSJeff Kirsher 
236fc21117SJose Abreu #include "dwxgmac2.h"
247ac6653aSJeff Kirsher #include "stmmac.h"
257ac6653aSJeff Kirsher 
267ac6653aSJeff Kirsher #define MII_BUSY 0x00000001
277ac6653aSJeff Kirsher #define MII_WRITE 0x00000002
28d4117d63SKweh Hock Leong #define MII_DATA_MASK GENMASK(15, 0)
297ac6653aSJeff Kirsher 
30ac1f74a7SAlexandre TORGUE /* GMAC4 defines */
31ac1f74a7SAlexandre TORGUE #define MII_GMAC4_GOC_SHIFT		2
32d4117d63SKweh Hock Leong #define MII_GMAC4_REG_ADDR_SHIFT	16
33ac1f74a7SAlexandre TORGUE #define MII_GMAC4_WRITE			(1 << MII_GMAC4_GOC_SHIFT)
34ac1f74a7SAlexandre TORGUE #define MII_GMAC4_READ			(3 << MII_GMAC4_GOC_SHIFT)
35d4117d63SKweh Hock Leong #define MII_GMAC4_C45E			BIT(1)
36ac1f74a7SAlexandre TORGUE 
376fc21117SJose Abreu /* XGMAC defines */
386fc21117SJose Abreu #define MII_XGMAC_SADDR			BIT(18)
396fc21117SJose Abreu #define MII_XGMAC_CMD_SHIFT		16
406fc21117SJose Abreu #define MII_XGMAC_WRITE			(1 << MII_XGMAC_CMD_SHIFT)
416fc21117SJose Abreu #define MII_XGMAC_READ			(3 << MII_XGMAC_CMD_SHIFT)
426fc21117SJose Abreu #define MII_XGMAC_BUSY			BIT(22)
436fc21117SJose Abreu #define MII_XGMAC_MAX_C22ADDR		3
446fc21117SJose Abreu #define MII_XGMAC_C22P_MASK		GENMASK(MII_XGMAC_MAX_C22ADDR, 0)
4504d1190aSJose Abreu #define MII_XGMAC_PA_SHIFT		16
4604d1190aSJose Abreu #define MII_XGMAC_DA_SHIFT		21
4704d1190aSJose Abreu 
4804d1190aSJose Abreu static int stmmac_xgmac2_c45_format(struct stmmac_priv *priv, int phyaddr,
4904d1190aSJose Abreu 				    int phyreg, u32 *hw_addr)
5004d1190aSJose Abreu {
5104d1190aSJose Abreu 	u32 tmp;
5204d1190aSJose Abreu 
5304d1190aSJose Abreu 	/* Set port as Clause 45 */
5404d1190aSJose Abreu 	tmp = readl(priv->ioaddr + XGMAC_MDIO_C22P);
5504d1190aSJose Abreu 	tmp &= ~BIT(phyaddr);
5604d1190aSJose Abreu 	writel(tmp, priv->ioaddr + XGMAC_MDIO_C22P);
5704d1190aSJose Abreu 
5804d1190aSJose Abreu 	*hw_addr = (phyaddr << MII_XGMAC_PA_SHIFT) | (phyreg & 0xffff);
5904d1190aSJose Abreu 	*hw_addr |= (phyreg >> MII_DEVADDR_C45_SHIFT) << MII_XGMAC_DA_SHIFT;
6004d1190aSJose Abreu 	return 0;
6104d1190aSJose Abreu }
626fc21117SJose Abreu 
636fc21117SJose Abreu static int stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int phyaddr,
646fc21117SJose Abreu 				    int phyreg, u32 *hw_addr)
656fc21117SJose Abreu {
666fc21117SJose Abreu 	u32 tmp;
676fc21117SJose Abreu 
686fc21117SJose Abreu 	/* HW does not support C22 addr >= 4 */
696fc21117SJose Abreu 	if (phyaddr > MII_XGMAC_MAX_C22ADDR)
706fc21117SJose Abreu 		return -ENODEV;
716fc21117SJose Abreu 
726fc21117SJose Abreu 	/* Set port as Clause 22 */
736fc21117SJose Abreu 	tmp = readl(priv->ioaddr + XGMAC_MDIO_C22P);
746fc21117SJose Abreu 	tmp &= ~MII_XGMAC_C22P_MASK;
756fc21117SJose Abreu 	tmp |= BIT(phyaddr);
766fc21117SJose Abreu 	writel(tmp, priv->ioaddr + XGMAC_MDIO_C22P);
776fc21117SJose Abreu 
7804d1190aSJose Abreu 	*hw_addr = (phyaddr << MII_XGMAC_PA_SHIFT) | (phyreg & 0x1f);
796fc21117SJose Abreu 	return 0;
806fc21117SJose Abreu }
816fc21117SJose Abreu 
826fc21117SJose Abreu static int stmmac_xgmac2_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
836fc21117SJose Abreu {
846fc21117SJose Abreu 	struct net_device *ndev = bus->priv;
856fc21117SJose Abreu 	struct stmmac_priv *priv = netdev_priv(ndev);
866fc21117SJose Abreu 	unsigned int mii_address = priv->hw->mii.addr;
876fc21117SJose Abreu 	unsigned int mii_data = priv->hw->mii.data;
886fc21117SJose Abreu 	u32 tmp, addr, value = MII_XGMAC_BUSY;
896fc21117SJose Abreu 	int ret;
906fc21117SJose Abreu 
91*e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
92*e2d0acd4SMinghao Chi 	if (ret < 0)
935ec55823SJoakim Zhang 		return ret;
945ec55823SJoakim Zhang 
9504d1190aSJose Abreu 	/* Wait until any existing MII operation is complete */
9604d1190aSJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
975ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
985ec55823SJoakim Zhang 		ret = -EBUSY;
995ec55823SJoakim Zhang 		goto err_disable_clks;
1005ec55823SJoakim Zhang 	}
10104d1190aSJose Abreu 
1026fc21117SJose Abreu 	if (phyreg & MII_ADDR_C45) {
10304d1190aSJose Abreu 		phyreg &= ~MII_ADDR_C45;
10404d1190aSJose Abreu 
10504d1190aSJose Abreu 		ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr);
10604d1190aSJose Abreu 		if (ret)
1075ec55823SJoakim Zhang 			goto err_disable_clks;
1086fc21117SJose Abreu 	} else {
1096fc21117SJose Abreu 		ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
1106fc21117SJose Abreu 		if (ret)
1115ec55823SJoakim Zhang 			goto err_disable_clks;
11204d1190aSJose Abreu 
11304d1190aSJose Abreu 		value |= MII_XGMAC_SADDR;
1146fc21117SJose Abreu 	}
1156fc21117SJose Abreu 
1166fc21117SJose Abreu 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
1176fc21117SJose Abreu 		& priv->hw->mii.clk_csr_mask;
11804d1190aSJose Abreu 	value |= MII_XGMAC_READ;
1196fc21117SJose Abreu 
1206fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1216fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1225ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1235ec55823SJoakim Zhang 		ret = -EBUSY;
1245ec55823SJoakim Zhang 		goto err_disable_clks;
1255ec55823SJoakim Zhang 	}
1266fc21117SJose Abreu 
1276fc21117SJose Abreu 	/* Set the MII address register to read */
1286fc21117SJose Abreu 	writel(addr, priv->ioaddr + mii_address);
1296fc21117SJose Abreu 	writel(value, priv->ioaddr + mii_data);
1306fc21117SJose Abreu 
1316fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1326fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1335ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1345ec55823SJoakim Zhang 		ret = -EBUSY;
1355ec55823SJoakim Zhang 		goto err_disable_clks;
1365ec55823SJoakim Zhang 	}
1376fc21117SJose Abreu 
1386fc21117SJose Abreu 	/* Read the data from the MII data register */
1395ec55823SJoakim Zhang 	ret = (int)readl(priv->ioaddr + mii_data) & GENMASK(15, 0);
1405ec55823SJoakim Zhang 
1415ec55823SJoakim Zhang err_disable_clks:
1425ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
1435ec55823SJoakim Zhang 
1445ec55823SJoakim Zhang 	return ret;
1456fc21117SJose Abreu }
1466fc21117SJose Abreu 
1476fc21117SJose Abreu static int stmmac_xgmac2_mdio_write(struct mii_bus *bus, int phyaddr,
1486fc21117SJose Abreu 				    int phyreg, u16 phydata)
1496fc21117SJose Abreu {
1506fc21117SJose Abreu 	struct net_device *ndev = bus->priv;
1516fc21117SJose Abreu 	struct stmmac_priv *priv = netdev_priv(ndev);
1526fc21117SJose Abreu 	unsigned int mii_address = priv->hw->mii.addr;
1536fc21117SJose Abreu 	unsigned int mii_data = priv->hw->mii.data;
1546fc21117SJose Abreu 	u32 addr, tmp, value = MII_XGMAC_BUSY;
1556fc21117SJose Abreu 	int ret;
1566fc21117SJose Abreu 
157*e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
158*e2d0acd4SMinghao Chi 	if (ret < 0)
1595ec55823SJoakim Zhang 		return ret;
1605ec55823SJoakim Zhang 
16104d1190aSJose Abreu 	/* Wait until any existing MII operation is complete */
16204d1190aSJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1635ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1645ec55823SJoakim Zhang 		ret = -EBUSY;
1655ec55823SJoakim Zhang 		goto err_disable_clks;
1665ec55823SJoakim Zhang 	}
16704d1190aSJose Abreu 
1686fc21117SJose Abreu 	if (phyreg & MII_ADDR_C45) {
16904d1190aSJose Abreu 		phyreg &= ~MII_ADDR_C45;
17004d1190aSJose Abreu 
17104d1190aSJose Abreu 		ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr);
17204d1190aSJose Abreu 		if (ret)
1735ec55823SJoakim Zhang 			goto err_disable_clks;
1746fc21117SJose Abreu 	} else {
1756fc21117SJose Abreu 		ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
1766fc21117SJose Abreu 		if (ret)
1775ec55823SJoakim Zhang 			goto err_disable_clks;
17804d1190aSJose Abreu 
17904d1190aSJose Abreu 		value |= MII_XGMAC_SADDR;
1806fc21117SJose Abreu 	}
1816fc21117SJose Abreu 
1826fc21117SJose Abreu 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
1836fc21117SJose Abreu 		& priv->hw->mii.clk_csr_mask;
18404d1190aSJose Abreu 	value |= phydata;
1856fc21117SJose Abreu 	value |= MII_XGMAC_WRITE;
1866fc21117SJose Abreu 
1876fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1886fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1895ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1905ec55823SJoakim Zhang 		ret = -EBUSY;
1915ec55823SJoakim Zhang 		goto err_disable_clks;
1925ec55823SJoakim Zhang 	}
1936fc21117SJose Abreu 
1946fc21117SJose Abreu 	/* Set the MII address register to write */
1956fc21117SJose Abreu 	writel(addr, priv->ioaddr + mii_address);
1966fc21117SJose Abreu 	writel(value, priv->ioaddr + mii_data);
1976fc21117SJose Abreu 
1986fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1995ec55823SJoakim Zhang 	ret = readl_poll_timeout(priv->ioaddr + mii_data, tmp,
2006fc21117SJose Abreu 				 !(tmp & MII_XGMAC_BUSY), 100, 10000);
2015ec55823SJoakim Zhang 
2025ec55823SJoakim Zhang err_disable_clks:
2035ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
2045ec55823SJoakim Zhang 
2055ec55823SJoakim Zhang 	return ret;
2066fc21117SJose Abreu }
2076fc21117SJose Abreu 
2087ac6653aSJeff Kirsher /**
2097ac6653aSJeff Kirsher  * stmmac_mdio_read
2107ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
211b91dce4cSLABBE Corentin  * @phyaddr: MII addr
212b91dce4cSLABBE Corentin  * @phyreg: MII reg
2137ac6653aSJeff Kirsher  * Description: it reads data from the MII register from within the phy device.
2147ac6653aSJeff Kirsher  * For the 7111 GMAC, we must set the bit 0 in the MII address register while
2157ac6653aSJeff Kirsher  * accessing the PHY registers.
2167ac6653aSJeff Kirsher  * Fortunately, it seems this has no drawback for the 7109 MAC.
2177ac6653aSJeff Kirsher  */
2187ac6653aSJeff Kirsher static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
2197ac6653aSJeff Kirsher {
2207ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
2217ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
2227ac6653aSJeff Kirsher 	unsigned int mii_address = priv->hw->mii.addr;
2237ac6653aSJeff Kirsher 	unsigned int mii_data = priv->hw->mii.data;
224b91dce4cSLABBE Corentin 	u32 value = MII_BUSY;
225d4117d63SKweh Hock Leong 	int data = 0;
226d4117d63SKweh Hock Leong 	u32 v;
227b91dce4cSLABBE Corentin 
228*e2d0acd4SMinghao Chi 	data = pm_runtime_resume_and_get(priv->device);
229*e2d0acd4SMinghao Chi 	if (data < 0)
2305ec55823SJoakim Zhang 		return data;
2315ec55823SJoakim Zhang 
232b91dce4cSLABBE Corentin 	value |= (phyaddr << priv->hw->mii.addr_shift)
233b91dce4cSLABBE Corentin 		& priv->hw->mii.addr_mask;
234b91dce4cSLABBE Corentin 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
235567be786Sjpinto 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
236567be786Sjpinto 		& priv->hw->mii.clk_csr_mask;
237d4117d63SKweh Hock Leong 	if (priv->plat->has_gmac4) {
238b91dce4cSLABBE Corentin 		value |= MII_GMAC4_READ;
239d4117d63SKweh Hock Leong 		if (phyreg & MII_ADDR_C45) {
240d4117d63SKweh Hock Leong 			value |= MII_GMAC4_C45E;
241d4117d63SKweh Hock Leong 			value &= ~priv->hw->mii.reg_mask;
242d4117d63SKweh Hock Leong 			value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) <<
243d4117d63SKweh Hock Leong 			       priv->hw->mii.reg_shift) &
244d4117d63SKweh Hock Leong 			       priv->hw->mii.reg_mask;
245d4117d63SKweh Hock Leong 
246d4117d63SKweh Hock Leong 			data |= (phyreg & MII_REGADDR_C45_MASK) <<
247d4117d63SKweh Hock Leong 				MII_GMAC4_REG_ADDR_SHIFT;
248d4117d63SKweh Hock Leong 		}
249d4117d63SKweh Hock Leong 	}
2507ac6653aSJeff Kirsher 
251a5f48adcSLABBE Corentin 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
2525ec55823SJoakim Zhang 			       100, 10000)) {
2535ec55823SJoakim Zhang 		data = -EBUSY;
2545ec55823SJoakim Zhang 		goto err_disable_clks;
2555ec55823SJoakim Zhang 	}
25639b401dbSDeepak SIKRI 
257d4117d63SKweh Hock Leong 	writel(data, priv->ioaddr + mii_data);
25801f1f615SLABBE Corentin 	writel(value, priv->ioaddr + mii_address);
25939b401dbSDeepak SIKRI 
260a5f48adcSLABBE Corentin 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
2615ec55823SJoakim Zhang 			       100, 10000)) {
2625ec55823SJoakim Zhang 		data = -EBUSY;
2635ec55823SJoakim Zhang 		goto err_disable_clks;
2645ec55823SJoakim Zhang 	}
2657ac6653aSJeff Kirsher 
2667ac6653aSJeff Kirsher 	/* Read the data from the MII data register */
267d4117d63SKweh Hock Leong 	data = (int)readl(priv->ioaddr + mii_data) & MII_DATA_MASK;
2687ac6653aSJeff Kirsher 
2695ec55823SJoakim Zhang err_disable_clks:
2705ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
2715ec55823SJoakim Zhang 
2727ac6653aSJeff Kirsher 	return data;
2737ac6653aSJeff Kirsher }
2747ac6653aSJeff Kirsher 
2757ac6653aSJeff Kirsher /**
2767ac6653aSJeff Kirsher  * stmmac_mdio_write
2777ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
278b91dce4cSLABBE Corentin  * @phyaddr: MII addr
279b91dce4cSLABBE Corentin  * @phyreg: MII reg
2807ac6653aSJeff Kirsher  * @phydata: phy data
2817ac6653aSJeff Kirsher  * Description: it writes the data into the MII register from within the device.
2827ac6653aSJeff Kirsher  */
2837ac6653aSJeff Kirsher static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
2847ac6653aSJeff Kirsher 			     u16 phydata)
2857ac6653aSJeff Kirsher {
2867ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
2877ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
2887ac6653aSJeff Kirsher 	unsigned int mii_address = priv->hw->mii.addr;
2897ac6653aSJeff Kirsher 	unsigned int mii_data = priv->hw->mii.data;
2905ec55823SJoakim Zhang 	int ret, data = phydata;
2915799fc90SKweh, Hock Leong 	u32 value = MII_BUSY;
292d4117d63SKweh Hock Leong 	u32 v;
2937ac6653aSJeff Kirsher 
294*e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
295*e2d0acd4SMinghao Chi 	if (ret < 0)
2965ec55823SJoakim Zhang 		return ret;
2975ec55823SJoakim Zhang 
298b91dce4cSLABBE Corentin 	value |= (phyaddr << priv->hw->mii.addr_shift)
299b91dce4cSLABBE Corentin 		& priv->hw->mii.addr_mask;
300b91dce4cSLABBE Corentin 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
3017ac6653aSJeff Kirsher 
302567be786Sjpinto 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
303567be786Sjpinto 		& priv->hw->mii.clk_csr_mask;
304d4117d63SKweh Hock Leong 	if (priv->plat->has_gmac4) {
305b91dce4cSLABBE Corentin 		value |= MII_GMAC4_WRITE;
306d4117d63SKweh Hock Leong 		if (phyreg & MII_ADDR_C45) {
307d4117d63SKweh Hock Leong 			value |= MII_GMAC4_C45E;
308d4117d63SKweh Hock Leong 			value &= ~priv->hw->mii.reg_mask;
309d4117d63SKweh Hock Leong 			value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) <<
310d4117d63SKweh Hock Leong 			       priv->hw->mii.reg_shift) &
311d4117d63SKweh Hock Leong 			       priv->hw->mii.reg_mask;
312d4117d63SKweh Hock Leong 
313d4117d63SKweh Hock Leong 			data |= (phyreg & MII_REGADDR_C45_MASK) <<
314d4117d63SKweh Hock Leong 				MII_GMAC4_REG_ADDR_SHIFT;
315d4117d63SKweh Hock Leong 		}
316d4117d63SKweh Hock Leong 	} else {
3175799fc90SKweh, Hock Leong 		value |= MII_WRITE;
318d4117d63SKweh Hock Leong 	}
319ac1f74a7SAlexandre TORGUE 
320ac1f74a7SAlexandre TORGUE 	/* Wait until any existing MII operation is complete */
321a5f48adcSLABBE Corentin 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
3225ec55823SJoakim Zhang 			       100, 10000)) {
3235ec55823SJoakim Zhang 		ret = -EBUSY;
3245ec55823SJoakim Zhang 		goto err_disable_clks;
3255ec55823SJoakim Zhang 	}
326ac1f74a7SAlexandre TORGUE 
327ac1f74a7SAlexandre TORGUE 	/* Set the MII address register to write */
328d4117d63SKweh Hock Leong 	writel(data, priv->ioaddr + mii_data);
329ac1f74a7SAlexandre TORGUE 	writel(value, priv->ioaddr + mii_address);
330ac1f74a7SAlexandre TORGUE 
331ac1f74a7SAlexandre TORGUE 	/* Wait until any existing MII operation is complete */
3325ec55823SJoakim Zhang 	ret = readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
333a5f48adcSLABBE Corentin 				 100, 10000);
3345ec55823SJoakim Zhang 
3355ec55823SJoakim Zhang err_disable_clks:
3365ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
3375ec55823SJoakim Zhang 
3385ec55823SJoakim Zhang 	return ret;
339ac1f74a7SAlexandre TORGUE }
340ac1f74a7SAlexandre TORGUE 
341ac1f74a7SAlexandre TORGUE /**
3427ac6653aSJeff Kirsher  * stmmac_mdio_reset
3437ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
3447ac6653aSJeff Kirsher  * Description: reset the MII bus
3457ac6653aSJeff Kirsher  */
346073752aaSSrinivas Kandagatla int stmmac_mdio_reset(struct mii_bus *bus)
3477ac6653aSJeff Kirsher {
34830549aabSNiklas Cassel #if IS_ENABLED(CONFIG_STMMAC_PLATFORM)
3497ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
3507ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
3517ac6653aSJeff Kirsher 	unsigned int mii_address = priv->hw->mii.addr;
3527ac6653aSJeff Kirsher 
3530e076471SSrinivas Kandagatla #ifdef CONFIG_OF
3540e076471SSrinivas Kandagatla 	if (priv->device->of_node) {
3557c86f20dSMartin Blumenstingl 		struct gpio_desc *reset_gpio;
35684ce4d0fSMartin Blumenstingl 		u32 delays[3] = { 0, 0, 0 };
3577c86f20dSMartin Blumenstingl 
3587c86f20dSMartin Blumenstingl 		reset_gpio = devm_gpiod_get_optional(priv->device,
3597c86f20dSMartin Blumenstingl 						     "snps,reset",
3607c86f20dSMartin Blumenstingl 						     GPIOD_OUT_LOW);
3617c86f20dSMartin Blumenstingl 		if (IS_ERR(reset_gpio))
3627c86f20dSMartin Blumenstingl 			return PTR_ERR(reset_gpio);
3630e076471SSrinivas Kandagatla 
364cc5e92c2SMartin Blumenstingl 		device_property_read_u32_array(priv->device,
36542a90766SMartin Blumenstingl 					       "snps,reset-delays-us",
366cc5e92c2SMartin Blumenstingl 					       delays, ARRAY_SIZE(delays));
3670e076471SSrinivas Kandagatla 
368ce4ab73aSMartin Blumenstingl 		if (delays[0])
369ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[0], 1000));
370892aa01dSSjoerd Simons 
3717c86f20dSMartin Blumenstingl 		gpiod_set_value_cansleep(reset_gpio, 1);
372ce4ab73aSMartin Blumenstingl 		if (delays[1])
373ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[1], 1000));
374892aa01dSSjoerd Simons 
3757c86f20dSMartin Blumenstingl 		gpiod_set_value_cansleep(reset_gpio, 0);
376ce4ab73aSMartin Blumenstingl 		if (delays[2])
377ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[2], 1000));
3780e076471SSrinivas Kandagatla 	}
3790e076471SSrinivas Kandagatla #endif
3800e076471SSrinivas Kandagatla 
3817ac6653aSJeff Kirsher 	/* This is a workaround for problems with the STE101P PHY.
3827ac6653aSJeff Kirsher 	 * It doesn't complete its reset until at least one clock cycle
3838d45e42bSLABBE Corentin 	 * on MDC, so perform a dummy mdio read. To be updated for GMAC4
384ac1f74a7SAlexandre TORGUE 	 * if needed.
3857ac6653aSJeff Kirsher 	 */
386ac1f74a7SAlexandre TORGUE 	if (!priv->plat->has_gmac4)
3877ac6653aSJeff Kirsher 		writel(0, priv->ioaddr + mii_address);
388bfab27a1SGiuseppe CAVALLARO #endif
3897ac6653aSJeff Kirsher 	return 0;
3907ac6653aSJeff Kirsher }
3917ac6653aSJeff Kirsher 
392597a68ceSVoon Weifeng int stmmac_xpcs_setup(struct mii_bus *bus)
393597a68ceSVoon Weifeng {
394597a68ceSVoon Weifeng 	struct net_device *ndev = bus->priv;
395597a68ceSVoon Weifeng 	struct mdio_device *mdiodev;
39647538dbeSVladimir Oltean 	struct stmmac_priv *priv;
39747538dbeSVladimir Oltean 	struct dw_xpcs *xpcs;
39847538dbeSVladimir Oltean 	int mode, addr;
399597a68ceSVoon Weifeng 
400597a68ceSVoon Weifeng 	priv = netdev_priv(ndev);
401597a68ceSVoon Weifeng 	mode = priv->plat->phy_interface;
402597a68ceSVoon Weifeng 
403597a68ceSVoon Weifeng 	/* Try to probe the XPCS by scanning all addresses. */
404597a68ceSVoon Weifeng 	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
405597a68ceSVoon Weifeng 		mdiodev = mdio_device_create(bus, addr);
406597a68ceSVoon Weifeng 		if (IS_ERR(mdiodev))
407597a68ceSVoon Weifeng 			continue;
408597a68ceSVoon Weifeng 
409597a68ceSVoon Weifeng 		xpcs = xpcs_create(mdiodev, mode);
410597a68ceSVoon Weifeng 		if (IS_ERR_OR_NULL(xpcs)) {
411597a68ceSVoon Weifeng 			mdio_device_free(mdiodev);
412597a68ceSVoon Weifeng 			continue;
413597a68ceSVoon Weifeng 		}
414597a68ceSVoon Weifeng 
415597a68ceSVoon Weifeng 		priv->hw->xpcs = xpcs;
416597a68ceSVoon Weifeng 		break;
417597a68ceSVoon Weifeng 	}
418597a68ceSVoon Weifeng 
419597a68ceSVoon Weifeng 	if (!priv->hw->xpcs) {
420597a68ceSVoon Weifeng 		dev_warn(priv->device, "No xPCS found\n");
421597a68ceSVoon Weifeng 		return -ENODEV;
422597a68ceSVoon Weifeng 	}
423597a68ceSVoon Weifeng 
424597a68ceSVoon Weifeng 	return 0;
425597a68ceSVoon Weifeng }
426597a68ceSVoon Weifeng 
4277ac6653aSJeff Kirsher /**
4287ac6653aSJeff Kirsher  * stmmac_mdio_register
4297ac6653aSJeff Kirsher  * @ndev: net device structure
4307ac6653aSJeff Kirsher  * Description: it registers the MII bus
4317ac6653aSJeff Kirsher  */
4327ac6653aSJeff Kirsher int stmmac_mdio_register(struct net_device *ndev)
4337ac6653aSJeff Kirsher {
4347ac6653aSJeff Kirsher 	int err = 0;
4357ac6653aSJeff Kirsher 	struct mii_bus *new_bus;
4367ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
4377ac6653aSJeff Kirsher 	struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
438a7657f12SGiuseppe CAVALLARO 	struct device_node *mdio_node = priv->plat->mdio_node;
439fbca1647SRomain Perier 	struct device *dev = ndev->dev.parent;
4406fc21117SJose Abreu 	int addr, found, max_addr;
4417ac6653aSJeff Kirsher 
4427ac6653aSJeff Kirsher 	if (!mdio_bus_data)
4437ac6653aSJeff Kirsher 		return 0;
4447ac6653aSJeff Kirsher 
4457ac6653aSJeff Kirsher 	new_bus = mdiobus_alloc();
446efd89b60SLABBE Corentin 	if (!new_bus)
4477ac6653aSJeff Kirsher 		return -ENOMEM;
4487ac6653aSJeff Kirsher 
449e7f4dc35SAndrew Lunn 	if (mdio_bus_data->irqs)
450643d60bfSMarek Vasut 		memcpy(new_bus->irq, mdio_bus_data->irqs, sizeof(new_bus->irq));
4517ac6653aSJeff Kirsher 
45290b9a545SAlessandro Rubini 	new_bus->name = "stmmac";
4536fc21117SJose Abreu 
454523437d7SWong Vee Khee 	if (priv->plat->has_gmac4)
455523437d7SWong Vee Khee 		new_bus->probe_capabilities = MDIOBUS_C22_C45;
456523437d7SWong Vee Khee 
4576fc21117SJose Abreu 	if (priv->plat->has_xgmac) {
4586fc21117SJose Abreu 		new_bus->read = &stmmac_xgmac2_mdio_read;
4596fc21117SJose Abreu 		new_bus->write = &stmmac_xgmac2_mdio_write;
4606fc21117SJose Abreu 
4616fc21117SJose Abreu 		/* Right now only C22 phys are supported */
4626fc21117SJose Abreu 		max_addr = MII_XGMAC_MAX_C22ADDR + 1;
4636fc21117SJose Abreu 
4646fc21117SJose Abreu 		/* Check if DT specified an unsupported phy addr */
4656fc21117SJose Abreu 		if (priv->plat->phy_addr > MII_XGMAC_MAX_C22ADDR)
4666fc21117SJose Abreu 			dev_err(dev, "Unsupported phy_addr (max=%d)\n",
4676fc21117SJose Abreu 					MII_XGMAC_MAX_C22ADDR);
4686fc21117SJose Abreu 	} else {
4697ac6653aSJeff Kirsher 		new_bus->read = &stmmac_mdio_read;
4707ac6653aSJeff Kirsher 		new_bus->write = &stmmac_mdio_write;
4716fc21117SJose Abreu 		max_addr = PHY_MAX_ADDR;
4726fc21117SJose Abreu 	}
473ac1f74a7SAlexandre TORGUE 
4741a981c05SThierry Reding 	if (mdio_bus_data->needs_reset)
4757ac6653aSJeff Kirsher 		new_bus->reset = &stmmac_mdio_reset;
4761a981c05SThierry Reding 
477db8857bfSFlorian Fainelli 	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
478d56631a6SSrinivas Kandagatla 		 new_bus->name, priv->plat->bus_id);
4797ac6653aSJeff Kirsher 	new_bus->priv = ndev;
4807ac6653aSJeff Kirsher 	new_bus->phy_mask = mdio_bus_data->phy_mask;
4817ac6653aSJeff Kirsher 	new_bus->parent = priv->device;
482e34d6569SPhil Reid 
483e34d6569SPhil Reid 	err = of_mdiobus_register(new_bus, mdio_node);
4847ac6653aSJeff Kirsher 	if (err != 0) {
485fbca1647SRomain Perier 		dev_err(dev, "Cannot register the MDIO bus\n");
4867ac6653aSJeff Kirsher 		goto bus_register_fail;
4877ac6653aSJeff Kirsher 	}
4887ac6653aSJeff Kirsher 
48904d1190aSJose Abreu 	/* Looks like we need a dummy read for XGMAC only and C45 PHYs */
49004d1190aSJose Abreu 	if (priv->plat->has_xgmac)
49104d1190aSJose Abreu 		stmmac_xgmac2_mdio_read(new_bus, 0, MII_ADDR_C45);
49204d1190aSJose Abreu 
493cc2fa619SPhil Reid 	if (priv->plat->phy_node || mdio_node)
494cc2fa619SPhil Reid 		goto bus_register_done;
495cc2fa619SPhil Reid 
4967ac6653aSJeff Kirsher 	found = 0;
4976fc21117SJose Abreu 	for (addr = 0; addr < max_addr; addr++) {
4987f854420SAndrew Lunn 		struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
4997ac6653aSJeff Kirsher 
500cc26dc67SLABBE Corentin 		if (!phydev)
501cc26dc67SLABBE Corentin 			continue;
502cc26dc67SLABBE Corentin 
5037ac6653aSJeff Kirsher 		/*
5047ac6653aSJeff Kirsher 		 * If an IRQ was provided to be assigned after
5057ac6653aSJeff Kirsher 		 * the bus probe, do it here.
5067ac6653aSJeff Kirsher 		 */
507cb2c0aceSLABBE Corentin 		if (!mdio_bus_data->irqs &&
5087ac6653aSJeff Kirsher 		    (mdio_bus_data->probed_phy_irq > 0)) {
509cc26dc67SLABBE Corentin 			new_bus->irq[addr] = mdio_bus_data->probed_phy_irq;
5107ac6653aSJeff Kirsher 			phydev->irq = mdio_bus_data->probed_phy_irq;
5117ac6653aSJeff Kirsher 		}
5127ac6653aSJeff Kirsher 
5137ac6653aSJeff Kirsher 		/*
5147ac6653aSJeff Kirsher 		 * If we're going to bind the MAC to this PHY bus,
5157ac6653aSJeff Kirsher 		 * and no PHY number was provided to the MAC,
5167ac6653aSJeff Kirsher 		 * use the one probed here.
5177ac6653aSJeff Kirsher 		 */
518d56631a6SSrinivas Kandagatla 		if (priv->plat->phy_addr == -1)
5197ac6653aSJeff Kirsher 			priv->plat->phy_addr = addr;
5207ac6653aSJeff Kirsher 
521fbca1647SRomain Perier 		phy_attached_info(phydev);
5227ac6653aSJeff Kirsher 		found = 1;
5237ac6653aSJeff Kirsher 	}
5247ac6653aSJeff Kirsher 
5254751d2aaSVladimir Oltean 	if (!found && !mdio_node) {
5264751d2aaSVladimir Oltean 		dev_warn(dev, "No PHY found\n");
5274751d2aaSVladimir Oltean 		err = -ENODEV;
5284751d2aaSVladimir Oltean 		goto no_phy_found;
5294751d2aaSVladimir Oltean 	}
5304751d2aaSVladimir Oltean 
531cc2fa619SPhil Reid bus_register_done:
5323955b22bSGiuseppe CAVALLARO 	priv->mii = new_bus;
5337ac6653aSJeff Kirsher 
5347ac6653aSJeff Kirsher 	return 0;
5357ac6653aSJeff Kirsher 
5364751d2aaSVladimir Oltean no_phy_found:
5374751d2aaSVladimir Oltean 	mdiobus_unregister(new_bus);
5387ac6653aSJeff Kirsher bus_register_fail:
5397ac6653aSJeff Kirsher 	mdiobus_free(new_bus);
5407ac6653aSJeff Kirsher 	return err;
5417ac6653aSJeff Kirsher }
5427ac6653aSJeff Kirsher 
5437ac6653aSJeff Kirsher /**
5447ac6653aSJeff Kirsher  * stmmac_mdio_unregister
5457ac6653aSJeff Kirsher  * @ndev: net device structure
5467ac6653aSJeff Kirsher  * Description: it unregisters the MII bus
5477ac6653aSJeff Kirsher  */
5487ac6653aSJeff Kirsher int stmmac_mdio_unregister(struct net_device *ndev)
5497ac6653aSJeff Kirsher {
5507ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
5517ac6653aSJeff Kirsher 
552a5cf5ce9SSrinivas Kandagatla 	if (!priv->mii)
553a5cf5ce9SSrinivas Kandagatla 		return 0;
554a5cf5ce9SSrinivas Kandagatla 
5552cac15daSVladimir Oltean 	if (priv->hw->xpcs) {
55611059740SVladimir Oltean 		mdio_device_free(priv->hw->xpcs->mdiodev);
55711059740SVladimir Oltean 		xpcs_destroy(priv->hw->xpcs);
5582cac15daSVladimir Oltean 	}
5592cac15daSVladimir Oltean 
5607ac6653aSJeff Kirsher 	mdiobus_unregister(priv->mii);
5617ac6653aSJeff Kirsher 	priv->mii->priv = NULL;
5627ac6653aSJeff Kirsher 	mdiobus_free(priv->mii);
5637ac6653aSJeff Kirsher 	priv->mii = NULL;
5647ac6653aSJeff Kirsher 
5657ac6653aSJeff Kirsher 	return 0;
5667ac6653aSJeff Kirsher }
567