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 
485b0a447eSAndrew Lunn static void stmmac_xgmac2_c45_format(struct stmmac_priv *priv, int phyaddr,
495b0a447eSAndrew Lunn 				     int devad, 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);
595b0a447eSAndrew Lunn 	*hw_addr |= devad << MII_XGMAC_DA_SHIFT;
6004d1190aSJose Abreu }
616fc21117SJose Abreu 
625b0a447eSAndrew Lunn static void stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int phyaddr,
636fc21117SJose Abreu 				     int phyreg, u32 *hw_addr)
646fc21117SJose Abreu {
656fc21117SJose Abreu 	u32 tmp;
666fc21117SJose Abreu 
676fc21117SJose Abreu 	/* Set port as Clause 22 */
686fc21117SJose Abreu 	tmp = readl(priv->ioaddr + XGMAC_MDIO_C22P);
696fc21117SJose Abreu 	tmp &= ~MII_XGMAC_C22P_MASK;
706fc21117SJose Abreu 	tmp |= BIT(phyaddr);
716fc21117SJose Abreu 	writel(tmp, priv->ioaddr + XGMAC_MDIO_C22P);
726fc21117SJose Abreu 
7304d1190aSJose Abreu 	*hw_addr = (phyaddr << MII_XGMAC_PA_SHIFT) | (phyreg & 0x1f);
746fc21117SJose Abreu }
756fc21117SJose Abreu 
765b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_read(struct stmmac_priv *priv, u32 addr,
775b0a447eSAndrew Lunn 				   u32 value)
786fc21117SJose Abreu {
796fc21117SJose Abreu 	unsigned int mii_address = priv->hw->mii.addr;
806fc21117SJose Abreu 	unsigned int mii_data = priv->hw->mii.data;
815b0a447eSAndrew Lunn 	u32 tmp;
826fc21117SJose Abreu 	int ret;
836fc21117SJose Abreu 
84e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
85e2d0acd4SMinghao Chi 	if (ret < 0)
865ec55823SJoakim Zhang 		return ret;
875ec55823SJoakim Zhang 
8804d1190aSJose Abreu 	/* Wait until any existing MII operation is complete */
8904d1190aSJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
905ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
915ec55823SJoakim Zhang 		ret = -EBUSY;
925ec55823SJoakim Zhang 		goto err_disable_clks;
935ec55823SJoakim Zhang 	}
9404d1190aSJose Abreu 
956fc21117SJose Abreu 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
966fc21117SJose Abreu 		& priv->hw->mii.clk_csr_mask;
9704d1190aSJose Abreu 	value |= MII_XGMAC_READ;
986fc21117SJose Abreu 
996fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1006fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1015ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1025ec55823SJoakim Zhang 		ret = -EBUSY;
1035ec55823SJoakim Zhang 		goto err_disable_clks;
1045ec55823SJoakim Zhang 	}
1056fc21117SJose Abreu 
1066fc21117SJose Abreu 	/* Set the MII address register to read */
1076fc21117SJose Abreu 	writel(addr, priv->ioaddr + mii_address);
1086fc21117SJose Abreu 	writel(value, priv->ioaddr + mii_data);
1096fc21117SJose Abreu 
1106fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1116fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1125ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1135ec55823SJoakim Zhang 		ret = -EBUSY;
1145ec55823SJoakim Zhang 		goto err_disable_clks;
1155ec55823SJoakim Zhang 	}
1166fc21117SJose Abreu 
1176fc21117SJose Abreu 	/* Read the data from the MII data register */
1185ec55823SJoakim Zhang 	ret = (int)readl(priv->ioaddr + mii_data) & GENMASK(15, 0);
1195ec55823SJoakim Zhang 
1205ec55823SJoakim Zhang err_disable_clks:
1215ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
1225ec55823SJoakim Zhang 
1235ec55823SJoakim Zhang 	return ret;
1246fc21117SJose Abreu }
1256fc21117SJose Abreu 
1265b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_read_c22(struct mii_bus *bus, int phyaddr,
1275b0a447eSAndrew Lunn 				       int phyreg)
1286fc21117SJose Abreu {
1296fc21117SJose Abreu 	struct net_device *ndev = bus->priv;
1305b0a447eSAndrew Lunn 	struct stmmac_priv *priv;
1315b0a447eSAndrew Lunn 	u32 addr;
1325b0a447eSAndrew Lunn 
1335b0a447eSAndrew Lunn 	priv = netdev_priv(ndev);
1345b0a447eSAndrew Lunn 
1355b0a447eSAndrew Lunn 	/* HW does not support C22 addr >= 4 */
1365b0a447eSAndrew Lunn 	if (phyaddr > MII_XGMAC_MAX_C22ADDR)
1375b0a447eSAndrew Lunn 		return -ENODEV;
1385b0a447eSAndrew Lunn 
1395b0a447eSAndrew Lunn 	stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
1405b0a447eSAndrew Lunn 
1415b0a447eSAndrew Lunn 	return stmmac_xgmac2_mdio_read(priv, addr, MII_XGMAC_BUSY);
1425b0a447eSAndrew Lunn }
1435b0a447eSAndrew Lunn 
1445b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_read_c45(struct mii_bus *bus, int phyaddr,
1455b0a447eSAndrew Lunn 				       int devad, int phyreg)
1465b0a447eSAndrew Lunn {
1475b0a447eSAndrew Lunn 	struct net_device *ndev = bus->priv;
1485b0a447eSAndrew Lunn 	struct stmmac_priv *priv;
1495b0a447eSAndrew Lunn 	u32 addr;
1505b0a447eSAndrew Lunn 
1515b0a447eSAndrew Lunn 	priv = netdev_priv(ndev);
1525b0a447eSAndrew Lunn 
1535b0a447eSAndrew Lunn 	stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr);
1545b0a447eSAndrew Lunn 
1555b0a447eSAndrew Lunn 	return stmmac_xgmac2_mdio_read(priv, addr, MII_XGMAC_BUSY);
1565b0a447eSAndrew Lunn }
1575b0a447eSAndrew Lunn 
1585b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_write(struct stmmac_priv *priv, u32 addr,
1595b0a447eSAndrew Lunn 				    u32 value, u16 phydata)
1605b0a447eSAndrew Lunn {
1616fc21117SJose Abreu 	unsigned int mii_address = priv->hw->mii.addr;
1626fc21117SJose Abreu 	unsigned int mii_data = priv->hw->mii.data;
1635b0a447eSAndrew Lunn 	u32 tmp;
1646fc21117SJose Abreu 	int ret;
1656fc21117SJose Abreu 
166e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
167e2d0acd4SMinghao Chi 	if (ret < 0)
1685ec55823SJoakim Zhang 		return ret;
1695ec55823SJoakim Zhang 
17004d1190aSJose Abreu 	/* Wait until any existing MII operation is complete */
17104d1190aSJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1725ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1735ec55823SJoakim Zhang 		ret = -EBUSY;
1745ec55823SJoakim Zhang 		goto err_disable_clks;
1755ec55823SJoakim Zhang 	}
17604d1190aSJose Abreu 
1776fc21117SJose Abreu 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
1786fc21117SJose Abreu 		& priv->hw->mii.clk_csr_mask;
17904d1190aSJose Abreu 	value |= phydata;
1806fc21117SJose Abreu 	value |= MII_XGMAC_WRITE;
1816fc21117SJose Abreu 
1826fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1836fc21117SJose Abreu 	if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1845ec55823SJoakim Zhang 			       !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
1855ec55823SJoakim Zhang 		ret = -EBUSY;
1865ec55823SJoakim Zhang 		goto err_disable_clks;
1875ec55823SJoakim Zhang 	}
1886fc21117SJose Abreu 
1896fc21117SJose Abreu 	/* Set the MII address register to write */
1906fc21117SJose Abreu 	writel(addr, priv->ioaddr + mii_address);
1916fc21117SJose Abreu 	writel(value, priv->ioaddr + mii_data);
1926fc21117SJose Abreu 
1936fc21117SJose Abreu 	/* Wait until any existing MII operation is complete */
1945ec55823SJoakim Zhang 	ret = readl_poll_timeout(priv->ioaddr + mii_data, tmp,
1956fc21117SJose Abreu 				 !(tmp & MII_XGMAC_BUSY), 100, 10000);
1965ec55823SJoakim Zhang 
1975ec55823SJoakim Zhang err_disable_clks:
1985ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
1995ec55823SJoakim Zhang 
2005ec55823SJoakim Zhang 	return ret;
2016fc21117SJose Abreu }
2026fc21117SJose Abreu 
2035b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_write_c22(struct mii_bus *bus, int phyaddr,
2045b0a447eSAndrew Lunn 					int phyreg, u16 phydata)
2055b0a447eSAndrew Lunn {
2065b0a447eSAndrew Lunn 	struct net_device *ndev = bus->priv;
2075b0a447eSAndrew Lunn 	struct stmmac_priv *priv;
2085b0a447eSAndrew Lunn 	u32 addr;
2095b0a447eSAndrew Lunn 
2105b0a447eSAndrew Lunn 	priv = netdev_priv(ndev);
2115b0a447eSAndrew Lunn 
2125b0a447eSAndrew Lunn 	/* HW does not support C22 addr >= 4 */
2135b0a447eSAndrew Lunn 	if (phyaddr > MII_XGMAC_MAX_C22ADDR)
2145b0a447eSAndrew Lunn 		return -ENODEV;
2155b0a447eSAndrew Lunn 
2165b0a447eSAndrew Lunn 	stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
2175b0a447eSAndrew Lunn 
2185b0a447eSAndrew Lunn 	return stmmac_xgmac2_mdio_write(priv, addr,
2195b0a447eSAndrew Lunn 					MII_XGMAC_BUSY | MII_XGMAC_SADDR, phydata);
2205b0a447eSAndrew Lunn }
2215b0a447eSAndrew Lunn 
2225b0a447eSAndrew Lunn static int stmmac_xgmac2_mdio_write_c45(struct mii_bus *bus, int phyaddr,
2235b0a447eSAndrew Lunn 					int devad, int phyreg, u16 phydata)
2245b0a447eSAndrew Lunn {
2255b0a447eSAndrew Lunn 	struct net_device *ndev = bus->priv;
2265b0a447eSAndrew Lunn 	struct stmmac_priv *priv;
2275b0a447eSAndrew Lunn 	u32 addr;
2285b0a447eSAndrew Lunn 
2295b0a447eSAndrew Lunn 	priv = netdev_priv(ndev);
2305b0a447eSAndrew Lunn 
2315b0a447eSAndrew Lunn 	stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr);
2325b0a447eSAndrew Lunn 
2335b0a447eSAndrew Lunn 	return stmmac_xgmac2_mdio_write(priv, addr, MII_XGMAC_BUSY,
2345b0a447eSAndrew Lunn 					phydata);
2355b0a447eSAndrew Lunn }
2365b0a447eSAndrew Lunn 
2373c7826d0SAndrew Lunn static int stmmac_mdio_read(struct stmmac_priv *priv, int data, u32 value)
2383c7826d0SAndrew Lunn {
2393c7826d0SAndrew Lunn 	unsigned int mii_address = priv->hw->mii.addr;
2403c7826d0SAndrew Lunn 	unsigned int mii_data = priv->hw->mii.data;
2413c7826d0SAndrew Lunn 	u32 v;
2423c7826d0SAndrew Lunn 
2433c7826d0SAndrew Lunn 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
2443c7826d0SAndrew Lunn 			       100, 10000))
2453c7826d0SAndrew Lunn 		return -EBUSY;
2463c7826d0SAndrew Lunn 
2473c7826d0SAndrew Lunn 	writel(data, priv->ioaddr + mii_data);
2483c7826d0SAndrew Lunn 	writel(value, priv->ioaddr + mii_address);
2493c7826d0SAndrew Lunn 
2503c7826d0SAndrew Lunn 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
2513c7826d0SAndrew Lunn 			       100, 10000))
2523c7826d0SAndrew Lunn 		return -EBUSY;
2533c7826d0SAndrew Lunn 
2543c7826d0SAndrew Lunn 	/* Read the data from the MII data register */
2553c7826d0SAndrew Lunn 	return readl(priv->ioaddr + mii_data) & MII_DATA_MASK;
2563c7826d0SAndrew Lunn }
2573c7826d0SAndrew Lunn 
2587ac6653aSJeff Kirsher /**
2593c7826d0SAndrew Lunn  * stmmac_mdio_read_c22
2607ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
261b91dce4cSLABBE Corentin  * @phyaddr: MII addr
262b91dce4cSLABBE Corentin  * @phyreg: MII reg
2637ac6653aSJeff Kirsher  * Description: it reads data from the MII register from within the phy device.
2647ac6653aSJeff Kirsher  * For the 7111 GMAC, we must set the bit 0 in the MII address register while
2657ac6653aSJeff Kirsher  * accessing the PHY registers.
2667ac6653aSJeff Kirsher  * Fortunately, it seems this has no drawback for the 7109 MAC.
2677ac6653aSJeff Kirsher  */
2683c7826d0SAndrew Lunn static int stmmac_mdio_read_c22(struct mii_bus *bus, int phyaddr, int phyreg)
2697ac6653aSJeff Kirsher {
2707ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
2717ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
272b91dce4cSLABBE Corentin 	u32 value = MII_BUSY;
273d4117d63SKweh Hock Leong 	int data = 0;
274b91dce4cSLABBE Corentin 
275e2d0acd4SMinghao Chi 	data = pm_runtime_resume_and_get(priv->device);
276e2d0acd4SMinghao Chi 	if (data < 0)
2775ec55823SJoakim Zhang 		return data;
2785ec55823SJoakim Zhang 
279b91dce4cSLABBE Corentin 	value |= (phyaddr << priv->hw->mii.addr_shift)
280b91dce4cSLABBE Corentin 		& priv->hw->mii.addr_mask;
281b91dce4cSLABBE Corentin 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
282567be786Sjpinto 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
283567be786Sjpinto 		& priv->hw->mii.clk_csr_mask;
284*7c6b942bSAndrew Halaney 	if (priv->plat->has_gmac4)
285b91dce4cSLABBE Corentin 		value |= MII_GMAC4_READ;
2867ac6653aSJeff Kirsher 
2873c7826d0SAndrew Lunn 	data = stmmac_mdio_read(priv, data, value);
28839b401dbSDeepak SIKRI 
2895ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
2905ec55823SJoakim Zhang 
2917ac6653aSJeff Kirsher 	return data;
2927ac6653aSJeff Kirsher }
2937ac6653aSJeff Kirsher 
2947ac6653aSJeff Kirsher /**
2953c7826d0SAndrew Lunn  * stmmac_mdio_read_c45
2963c7826d0SAndrew Lunn  * @bus: points to the mii_bus structure
2973c7826d0SAndrew Lunn  * @phyaddr: MII addr
2983c7826d0SAndrew Lunn  * @devad: device address to read
2993c7826d0SAndrew Lunn  * @phyreg: MII reg
3003c7826d0SAndrew Lunn  * Description: it reads data from the MII register from within the phy device.
3013c7826d0SAndrew Lunn  * For the 7111 GMAC, we must set the bit 0 in the MII address register while
3023c7826d0SAndrew Lunn  * accessing the PHY registers.
3033c7826d0SAndrew Lunn  * Fortunately, it seems this has no drawback for the 7109 MAC.
3043c7826d0SAndrew Lunn  */
3053c7826d0SAndrew Lunn static int stmmac_mdio_read_c45(struct mii_bus *bus, int phyaddr, int devad,
3063c7826d0SAndrew Lunn 				int phyreg)
3073c7826d0SAndrew Lunn {
3083c7826d0SAndrew Lunn 	struct net_device *ndev = bus->priv;
3093c7826d0SAndrew Lunn 	struct stmmac_priv *priv = netdev_priv(ndev);
3103c7826d0SAndrew Lunn 	u32 value = MII_BUSY;
3113c7826d0SAndrew Lunn 	int data = 0;
3123c7826d0SAndrew Lunn 
3133c7826d0SAndrew Lunn 	data = pm_runtime_get_sync(priv->device);
3143c7826d0SAndrew Lunn 	if (data < 0) {
3153c7826d0SAndrew Lunn 		pm_runtime_put_noidle(priv->device);
3163c7826d0SAndrew Lunn 		return data;
3173c7826d0SAndrew Lunn 	}
3183c7826d0SAndrew Lunn 
3193c7826d0SAndrew Lunn 	value |= (phyaddr << priv->hw->mii.addr_shift)
3203c7826d0SAndrew Lunn 		& priv->hw->mii.addr_mask;
3213c7826d0SAndrew Lunn 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
3223c7826d0SAndrew Lunn 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
3233c7826d0SAndrew Lunn 		& priv->hw->mii.clk_csr_mask;
3243c7826d0SAndrew Lunn 	value |= MII_GMAC4_READ;
3253c7826d0SAndrew Lunn 	value |= MII_GMAC4_C45E;
3263c7826d0SAndrew Lunn 	value &= ~priv->hw->mii.reg_mask;
3273c7826d0SAndrew Lunn 	value |= (devad << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
3283c7826d0SAndrew Lunn 
3293c7826d0SAndrew Lunn 	data |= phyreg << MII_GMAC4_REG_ADDR_SHIFT;
3303c7826d0SAndrew Lunn 
3313c7826d0SAndrew Lunn 	data = stmmac_mdio_read(priv, data, value);
3323c7826d0SAndrew Lunn 
3333c7826d0SAndrew Lunn 	pm_runtime_put(priv->device);
3343c7826d0SAndrew Lunn 
3353c7826d0SAndrew Lunn 	return data;
3363c7826d0SAndrew Lunn }
3373c7826d0SAndrew Lunn 
3383c7826d0SAndrew Lunn static int stmmac_mdio_write(struct stmmac_priv *priv, int data, u32 value)
3393c7826d0SAndrew Lunn {
3403c7826d0SAndrew Lunn 	unsigned int mii_address = priv->hw->mii.addr;
3413c7826d0SAndrew Lunn 	unsigned int mii_data = priv->hw->mii.data;
3423c7826d0SAndrew Lunn 	u32 v;
3433c7826d0SAndrew Lunn 
3443c7826d0SAndrew Lunn 	/* Wait until any existing MII operation is complete */
3453c7826d0SAndrew Lunn 	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
3463c7826d0SAndrew Lunn 			       100, 10000))
3473c7826d0SAndrew Lunn 		return -EBUSY;
3483c7826d0SAndrew Lunn 
3493c7826d0SAndrew Lunn 	/* Set the MII address register to write */
3503c7826d0SAndrew Lunn 	writel(data, priv->ioaddr + mii_data);
3513c7826d0SAndrew Lunn 	writel(value, priv->ioaddr + mii_address);
3523c7826d0SAndrew Lunn 
3533c7826d0SAndrew Lunn 	/* Wait until any existing MII operation is complete */
3543c7826d0SAndrew Lunn 	return readl_poll_timeout(priv->ioaddr + mii_address, v,
3553c7826d0SAndrew Lunn 				  !(v & MII_BUSY), 100, 10000);
3563c7826d0SAndrew Lunn }
3573c7826d0SAndrew Lunn 
3583c7826d0SAndrew Lunn /**
3593c7826d0SAndrew Lunn  * stmmac_mdio_write_c22
3607ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
361b91dce4cSLABBE Corentin  * @phyaddr: MII addr
362b91dce4cSLABBE Corentin  * @phyreg: MII reg
3637ac6653aSJeff Kirsher  * @phydata: phy data
3647ac6653aSJeff Kirsher  * Description: it writes the data into the MII register from within the device.
3657ac6653aSJeff Kirsher  */
3663c7826d0SAndrew Lunn static int stmmac_mdio_write_c22(struct mii_bus *bus, int phyaddr, int phyreg,
3677ac6653aSJeff Kirsher 				 u16 phydata)
3687ac6653aSJeff Kirsher {
3697ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
3707ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
3715ec55823SJoakim Zhang 	int ret, data = phydata;
3725799fc90SKweh, Hock Leong 	u32 value = MII_BUSY;
3737ac6653aSJeff Kirsher 
374e2d0acd4SMinghao Chi 	ret = pm_runtime_resume_and_get(priv->device);
375e2d0acd4SMinghao Chi 	if (ret < 0)
3765ec55823SJoakim Zhang 		return ret;
3775ec55823SJoakim Zhang 
378b91dce4cSLABBE Corentin 	value |= (phyaddr << priv->hw->mii.addr_shift)
379b91dce4cSLABBE Corentin 		& priv->hw->mii.addr_mask;
380b91dce4cSLABBE Corentin 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
3817ac6653aSJeff Kirsher 
382567be786Sjpinto 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
383567be786Sjpinto 		& priv->hw->mii.clk_csr_mask;
3843c7826d0SAndrew Lunn 	if (priv->plat->has_gmac4)
385b91dce4cSLABBE Corentin 		value |= MII_GMAC4_WRITE;
3863c7826d0SAndrew Lunn 	else
3873c7826d0SAndrew Lunn 		value |= MII_WRITE;
3883c7826d0SAndrew Lunn 
3893c7826d0SAndrew Lunn 	ret = stmmac_mdio_write(priv, data, value);
3903c7826d0SAndrew Lunn 
3913c7826d0SAndrew Lunn 	pm_runtime_put(priv->device);
3923c7826d0SAndrew Lunn 
3933c7826d0SAndrew Lunn 	return ret;
3943c7826d0SAndrew Lunn }
3953c7826d0SAndrew Lunn 
3963c7826d0SAndrew Lunn /**
3973c7826d0SAndrew Lunn  * stmmac_mdio_write_c45
3983c7826d0SAndrew Lunn  * @bus: points to the mii_bus structure
3993c7826d0SAndrew Lunn  * @phyaddr: MII addr
4003c7826d0SAndrew Lunn  * @phyreg: MII reg
4013c7826d0SAndrew Lunn  * @devad: device address to read
4023c7826d0SAndrew Lunn  * @phydata: phy data
4033c7826d0SAndrew Lunn  * Description: it writes the data into the MII register from within the device.
4043c7826d0SAndrew Lunn  */
4053c7826d0SAndrew Lunn static int stmmac_mdio_write_c45(struct mii_bus *bus, int phyaddr,
4063c7826d0SAndrew Lunn 				 int devad, int phyreg, u16 phydata)
4073c7826d0SAndrew Lunn {
4083c7826d0SAndrew Lunn 	struct net_device *ndev = bus->priv;
4093c7826d0SAndrew Lunn 	struct stmmac_priv *priv = netdev_priv(ndev);
4103c7826d0SAndrew Lunn 	int ret, data = phydata;
4113c7826d0SAndrew Lunn 	u32 value = MII_BUSY;
4123c7826d0SAndrew Lunn 
4133c7826d0SAndrew Lunn 	ret = pm_runtime_get_sync(priv->device);
4143c7826d0SAndrew Lunn 	if (ret < 0) {
4153c7826d0SAndrew Lunn 		pm_runtime_put_noidle(priv->device);
4163c7826d0SAndrew Lunn 		return ret;
4173c7826d0SAndrew Lunn 	}
4183c7826d0SAndrew Lunn 
4193c7826d0SAndrew Lunn 	value |= (phyaddr << priv->hw->mii.addr_shift)
4203c7826d0SAndrew Lunn 		& priv->hw->mii.addr_mask;
4213c7826d0SAndrew Lunn 	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
4223c7826d0SAndrew Lunn 
4233c7826d0SAndrew Lunn 	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
4243c7826d0SAndrew Lunn 		& priv->hw->mii.clk_csr_mask;
4253c7826d0SAndrew Lunn 
4263c7826d0SAndrew Lunn 	value |= MII_GMAC4_WRITE;
427d4117d63SKweh Hock Leong 	value |= MII_GMAC4_C45E;
428d4117d63SKweh Hock Leong 	value &= ~priv->hw->mii.reg_mask;
4293c7826d0SAndrew Lunn 	value |= (devad << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
430d4117d63SKweh Hock Leong 
4313c7826d0SAndrew Lunn 	data |= phyreg << MII_GMAC4_REG_ADDR_SHIFT;
432ac1f74a7SAlexandre TORGUE 
4333c7826d0SAndrew Lunn 	ret = stmmac_mdio_write(priv, data, value);
434ac1f74a7SAlexandre TORGUE 
4355ec55823SJoakim Zhang 	pm_runtime_put(priv->device);
4365ec55823SJoakim Zhang 
4375ec55823SJoakim Zhang 	return ret;
438ac1f74a7SAlexandre TORGUE }
439ac1f74a7SAlexandre TORGUE 
440ac1f74a7SAlexandre TORGUE /**
4417ac6653aSJeff Kirsher  * stmmac_mdio_reset
4427ac6653aSJeff Kirsher  * @bus: points to the mii_bus structure
4437ac6653aSJeff Kirsher  * Description: reset the MII bus
4447ac6653aSJeff Kirsher  */
445073752aaSSrinivas Kandagatla int stmmac_mdio_reset(struct mii_bus *bus)
4467ac6653aSJeff Kirsher {
44730549aabSNiklas Cassel #if IS_ENABLED(CONFIG_STMMAC_PLATFORM)
4487ac6653aSJeff Kirsher 	struct net_device *ndev = bus->priv;
4497ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
4507ac6653aSJeff Kirsher 	unsigned int mii_address = priv->hw->mii.addr;
4517ac6653aSJeff Kirsher 
4520e076471SSrinivas Kandagatla #ifdef CONFIG_OF
4530e076471SSrinivas Kandagatla 	if (priv->device->of_node) {
4547c86f20dSMartin Blumenstingl 		struct gpio_desc *reset_gpio;
45584ce4d0fSMartin Blumenstingl 		u32 delays[3] = { 0, 0, 0 };
4567c86f20dSMartin Blumenstingl 
4577c86f20dSMartin Blumenstingl 		reset_gpio = devm_gpiod_get_optional(priv->device,
4587c86f20dSMartin Blumenstingl 						     "snps,reset",
4597c86f20dSMartin Blumenstingl 						     GPIOD_OUT_LOW);
4607c86f20dSMartin Blumenstingl 		if (IS_ERR(reset_gpio))
4617c86f20dSMartin Blumenstingl 			return PTR_ERR(reset_gpio);
4620e076471SSrinivas Kandagatla 
463cc5e92c2SMartin Blumenstingl 		device_property_read_u32_array(priv->device,
46442a90766SMartin Blumenstingl 					       "snps,reset-delays-us",
465cc5e92c2SMartin Blumenstingl 					       delays, ARRAY_SIZE(delays));
4660e076471SSrinivas Kandagatla 
467ce4ab73aSMartin Blumenstingl 		if (delays[0])
468ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[0], 1000));
469892aa01dSSjoerd Simons 
4707c86f20dSMartin Blumenstingl 		gpiod_set_value_cansleep(reset_gpio, 1);
471ce4ab73aSMartin Blumenstingl 		if (delays[1])
472ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[1], 1000));
473892aa01dSSjoerd Simons 
4747c86f20dSMartin Blumenstingl 		gpiod_set_value_cansleep(reset_gpio, 0);
475ce4ab73aSMartin Blumenstingl 		if (delays[2])
476ce4ab73aSMartin Blumenstingl 			msleep(DIV_ROUND_UP(delays[2], 1000));
4770e076471SSrinivas Kandagatla 	}
4780e076471SSrinivas Kandagatla #endif
4790e076471SSrinivas Kandagatla 
4807ac6653aSJeff Kirsher 	/* This is a workaround for problems with the STE101P PHY.
4817ac6653aSJeff Kirsher 	 * It doesn't complete its reset until at least one clock cycle
4828d45e42bSLABBE Corentin 	 * on MDC, so perform a dummy mdio read. To be updated for GMAC4
483ac1f74a7SAlexandre TORGUE 	 * if needed.
4847ac6653aSJeff Kirsher 	 */
485ac1f74a7SAlexandre TORGUE 	if (!priv->plat->has_gmac4)
4867ac6653aSJeff Kirsher 		writel(0, priv->ioaddr + mii_address);
487bfab27a1SGiuseppe CAVALLARO #endif
4887ac6653aSJeff Kirsher 	return 0;
4897ac6653aSJeff Kirsher }
4907ac6653aSJeff Kirsher 
491597a68ceSVoon Weifeng int stmmac_xpcs_setup(struct mii_bus *bus)
492597a68ceSVoon Weifeng {
493597a68ceSVoon Weifeng 	struct net_device *ndev = bus->priv;
494597a68ceSVoon Weifeng 	struct mdio_device *mdiodev;
49547538dbeSVladimir Oltean 	struct stmmac_priv *priv;
49647538dbeSVladimir Oltean 	struct dw_xpcs *xpcs;
49747538dbeSVladimir Oltean 	int mode, addr;
498597a68ceSVoon Weifeng 
499597a68ceSVoon Weifeng 	priv = netdev_priv(ndev);
500597a68ceSVoon Weifeng 	mode = priv->plat->phy_interface;
501597a68ceSVoon Weifeng 
502597a68ceSVoon Weifeng 	/* Try to probe the XPCS by scanning all addresses. */
503597a68ceSVoon Weifeng 	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
504597a68ceSVoon Weifeng 		mdiodev = mdio_device_create(bus, addr);
505597a68ceSVoon Weifeng 		if (IS_ERR(mdiodev))
506597a68ceSVoon Weifeng 			continue;
507597a68ceSVoon Weifeng 
508597a68ceSVoon Weifeng 		xpcs = xpcs_create(mdiodev, mode);
509597a68ceSVoon Weifeng 		if (IS_ERR_OR_NULL(xpcs)) {
510597a68ceSVoon Weifeng 			mdio_device_free(mdiodev);
511597a68ceSVoon Weifeng 			continue;
512597a68ceSVoon Weifeng 		}
513597a68ceSVoon Weifeng 
514597a68ceSVoon Weifeng 		priv->hw->xpcs = xpcs;
515597a68ceSVoon Weifeng 		break;
516597a68ceSVoon Weifeng 	}
517597a68ceSVoon Weifeng 
518597a68ceSVoon Weifeng 	if (!priv->hw->xpcs) {
519597a68ceSVoon Weifeng 		dev_warn(priv->device, "No xPCS found\n");
520597a68ceSVoon Weifeng 		return -ENODEV;
521597a68ceSVoon Weifeng 	}
522597a68ceSVoon Weifeng 
523597a68ceSVoon Weifeng 	return 0;
524597a68ceSVoon Weifeng }
525597a68ceSVoon Weifeng 
5267ac6653aSJeff Kirsher /**
5277ac6653aSJeff Kirsher  * stmmac_mdio_register
5287ac6653aSJeff Kirsher  * @ndev: net device structure
5297ac6653aSJeff Kirsher  * Description: it registers the MII bus
5307ac6653aSJeff Kirsher  */
5317ac6653aSJeff Kirsher int stmmac_mdio_register(struct net_device *ndev)
5327ac6653aSJeff Kirsher {
5337ac6653aSJeff Kirsher 	int err = 0;
5347ac6653aSJeff Kirsher 	struct mii_bus *new_bus;
5357ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
536ab21cf92SOng Boon Leong 	struct fwnode_handle *fwnode = of_fwnode_handle(priv->plat->phylink_node);
5377ac6653aSJeff Kirsher 	struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
538a7657f12SGiuseppe CAVALLARO 	struct device_node *mdio_node = priv->plat->mdio_node;
539fbca1647SRomain Perier 	struct device *dev = ndev->dev.parent;
540ab21cf92SOng Boon Leong 	struct fwnode_handle *fixed_node;
5416fc21117SJose Abreu 	int addr, found, max_addr;
5427ac6653aSJeff Kirsher 
5437ac6653aSJeff Kirsher 	if (!mdio_bus_data)
5447ac6653aSJeff Kirsher 		return 0;
5457ac6653aSJeff Kirsher 
5467ac6653aSJeff Kirsher 	new_bus = mdiobus_alloc();
547efd89b60SLABBE Corentin 	if (!new_bus)
5487ac6653aSJeff Kirsher 		return -ENOMEM;
5497ac6653aSJeff Kirsher 
550e7f4dc35SAndrew Lunn 	if (mdio_bus_data->irqs)
551643d60bfSMarek Vasut 		memcpy(new_bus->irq, mdio_bus_data->irqs, sizeof(new_bus->irq));
5527ac6653aSJeff Kirsher 
55390b9a545SAlessandro Rubini 	new_bus->name = "stmmac";
5546fc21117SJose Abreu 
5556fc21117SJose Abreu 	if (priv->plat->has_xgmac) {
5565b0a447eSAndrew Lunn 		new_bus->read = &stmmac_xgmac2_mdio_read_c22;
5575b0a447eSAndrew Lunn 		new_bus->write = &stmmac_xgmac2_mdio_write_c22;
5585b0a447eSAndrew Lunn 		new_bus->read_c45 = &stmmac_xgmac2_mdio_read_c45;
5595b0a447eSAndrew Lunn 		new_bus->write_c45 = &stmmac_xgmac2_mdio_write_c45;
5606fc21117SJose Abreu 
5616fc21117SJose Abreu 		/* Right now only C22 phys are supported */
5626fc21117SJose Abreu 		max_addr = MII_XGMAC_MAX_C22ADDR + 1;
5636fc21117SJose Abreu 
5646fc21117SJose Abreu 		/* Check if DT specified an unsupported phy addr */
5656fc21117SJose Abreu 		if (priv->plat->phy_addr > MII_XGMAC_MAX_C22ADDR)
5666fc21117SJose Abreu 			dev_err(dev, "Unsupported phy_addr (max=%d)\n",
5676fc21117SJose Abreu 					MII_XGMAC_MAX_C22ADDR);
5686fc21117SJose Abreu 	} else {
5693c7826d0SAndrew Lunn 		new_bus->read = &stmmac_mdio_read_c22;
5703c7826d0SAndrew Lunn 		new_bus->write = &stmmac_mdio_write_c22;
5713c7826d0SAndrew Lunn 		if (priv->plat->has_gmac4) {
5723c7826d0SAndrew Lunn 			new_bus->read_c45 = &stmmac_mdio_read_c45;
5733c7826d0SAndrew Lunn 			new_bus->write_c45 = &stmmac_mdio_write_c45;
5743c7826d0SAndrew Lunn 		}
5753c7826d0SAndrew Lunn 
5766fc21117SJose Abreu 		max_addr = PHY_MAX_ADDR;
5776fc21117SJose Abreu 	}
578ac1f74a7SAlexandre TORGUE 
5791a981c05SThierry Reding 	if (mdio_bus_data->needs_reset)
5807ac6653aSJeff Kirsher 		new_bus->reset = &stmmac_mdio_reset;
5811a981c05SThierry Reding 
582db8857bfSFlorian Fainelli 	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
583d56631a6SSrinivas Kandagatla 		 new_bus->name, priv->plat->bus_id);
5847ac6653aSJeff Kirsher 	new_bus->priv = ndev;
5857ac6653aSJeff Kirsher 	new_bus->phy_mask = mdio_bus_data->phy_mask;
5867ac6653aSJeff Kirsher 	new_bus->parent = priv->device;
587e34d6569SPhil Reid 
588e34d6569SPhil Reid 	err = of_mdiobus_register(new_bus, mdio_node);
5897ac6653aSJeff Kirsher 	if (err != 0) {
590839612d2SRasmus Villemoes 		dev_err_probe(dev, err, "Cannot register the MDIO bus\n");
5917ac6653aSJeff Kirsher 		goto bus_register_fail;
5927ac6653aSJeff Kirsher 	}
5937ac6653aSJeff Kirsher 
59404d1190aSJose Abreu 	/* Looks like we need a dummy read for XGMAC only and C45 PHYs */
59504d1190aSJose Abreu 	if (priv->plat->has_xgmac)
5965b0a447eSAndrew Lunn 		stmmac_xgmac2_mdio_read_c45(new_bus, 0, 0, 0);
59704d1190aSJose Abreu 
598ab21cf92SOng Boon Leong 	/* If fixed-link is set, skip PHY scanning */
599ab21cf92SOng Boon Leong 	if (!fwnode)
600ab21cf92SOng Boon Leong 		fwnode = dev_fwnode(priv->device);
601ab21cf92SOng Boon Leong 
602ab21cf92SOng Boon Leong 	if (fwnode) {
603ab21cf92SOng Boon Leong 		fixed_node = fwnode_get_named_child_node(fwnode, "fixed-link");
604ab21cf92SOng Boon Leong 		if (fixed_node) {
605ab21cf92SOng Boon Leong 			fwnode_handle_put(fixed_node);
606ab21cf92SOng Boon Leong 			goto bus_register_done;
607ab21cf92SOng Boon Leong 		}
608ab21cf92SOng Boon Leong 	}
609ab21cf92SOng Boon Leong 
610cc2fa619SPhil Reid 	if (priv->plat->phy_node || mdio_node)
611cc2fa619SPhil Reid 		goto bus_register_done;
612cc2fa619SPhil Reid 
6137ac6653aSJeff Kirsher 	found = 0;
6146fc21117SJose Abreu 	for (addr = 0; addr < max_addr; addr++) {
6157f854420SAndrew Lunn 		struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
6167ac6653aSJeff Kirsher 
617cc26dc67SLABBE Corentin 		if (!phydev)
618cc26dc67SLABBE Corentin 			continue;
619cc26dc67SLABBE Corentin 
6207ac6653aSJeff Kirsher 		/*
6217ac6653aSJeff Kirsher 		 * If an IRQ was provided to be assigned after
6227ac6653aSJeff Kirsher 		 * the bus probe, do it here.
6237ac6653aSJeff Kirsher 		 */
624cb2c0aceSLABBE Corentin 		if (!mdio_bus_data->irqs &&
6257ac6653aSJeff Kirsher 		    (mdio_bus_data->probed_phy_irq > 0)) {
626cc26dc67SLABBE Corentin 			new_bus->irq[addr] = mdio_bus_data->probed_phy_irq;
6277ac6653aSJeff Kirsher 			phydev->irq = mdio_bus_data->probed_phy_irq;
6287ac6653aSJeff Kirsher 		}
6297ac6653aSJeff Kirsher 
6307ac6653aSJeff Kirsher 		/*
6317ac6653aSJeff Kirsher 		 * If we're going to bind the MAC to this PHY bus,
6327ac6653aSJeff Kirsher 		 * and no PHY number was provided to the MAC,
6337ac6653aSJeff Kirsher 		 * use the one probed here.
6347ac6653aSJeff Kirsher 		 */
635d56631a6SSrinivas Kandagatla 		if (priv->plat->phy_addr == -1)
6367ac6653aSJeff Kirsher 			priv->plat->phy_addr = addr;
6377ac6653aSJeff Kirsher 
638fbca1647SRomain Perier 		phy_attached_info(phydev);
6397ac6653aSJeff Kirsher 		found = 1;
6407ac6653aSJeff Kirsher 	}
6417ac6653aSJeff Kirsher 
6424751d2aaSVladimir Oltean 	if (!found && !mdio_node) {
6434751d2aaSVladimir Oltean 		dev_warn(dev, "No PHY found\n");
6444751d2aaSVladimir Oltean 		err = -ENODEV;
6454751d2aaSVladimir Oltean 		goto no_phy_found;
6464751d2aaSVladimir Oltean 	}
6474751d2aaSVladimir Oltean 
648cc2fa619SPhil Reid bus_register_done:
6493955b22bSGiuseppe CAVALLARO 	priv->mii = new_bus;
6507ac6653aSJeff Kirsher 
6517ac6653aSJeff Kirsher 	return 0;
6527ac6653aSJeff Kirsher 
6534751d2aaSVladimir Oltean no_phy_found:
6544751d2aaSVladimir Oltean 	mdiobus_unregister(new_bus);
6557ac6653aSJeff Kirsher bus_register_fail:
6567ac6653aSJeff Kirsher 	mdiobus_free(new_bus);
6577ac6653aSJeff Kirsher 	return err;
6587ac6653aSJeff Kirsher }
6597ac6653aSJeff Kirsher 
6607ac6653aSJeff Kirsher /**
6617ac6653aSJeff Kirsher  * stmmac_mdio_unregister
6627ac6653aSJeff Kirsher  * @ndev: net device structure
6637ac6653aSJeff Kirsher  * Description: it unregisters the MII bus
6647ac6653aSJeff Kirsher  */
6657ac6653aSJeff Kirsher int stmmac_mdio_unregister(struct net_device *ndev)
6667ac6653aSJeff Kirsher {
6677ac6653aSJeff Kirsher 	struct stmmac_priv *priv = netdev_priv(ndev);
6687ac6653aSJeff Kirsher 
669a5cf5ce9SSrinivas Kandagatla 	if (!priv->mii)
670a5cf5ce9SSrinivas Kandagatla 		return 0;
671a5cf5ce9SSrinivas Kandagatla 
6722cac15daSVladimir Oltean 	if (priv->hw->xpcs) {
67311059740SVladimir Oltean 		mdio_device_free(priv->hw->xpcs->mdiodev);
67411059740SVladimir Oltean 		xpcs_destroy(priv->hw->xpcs);
6752cac15daSVladimir Oltean 	}
6762cac15daSVladimir Oltean 
6777ac6653aSJeff Kirsher 	mdiobus_unregister(priv->mii);
6787ac6653aSJeff Kirsher 	priv->mii->priv = NULL;
6797ac6653aSJeff Kirsher 	mdiobus_free(priv->mii);
6807ac6653aSJeff Kirsher 	priv->mii = NULL;
6817ac6653aSJeff Kirsher 
6827ac6653aSJeff Kirsher 	return 0;
6837ac6653aSJeff Kirsher }
684