Эх сурвалжийг харах

Merge branch 'stmmac-probe-error-handling-and-phydev-leaks'

Johan Hovold says:

====================
net: stmmac: fix probe error handling and phydev leaks

This series fixes a number of issues with the stmmac-driver probe error
handling, which for example left clocks enabled after probe failures.

The final patch fixes a failure to deregister and free any fixed-link
PHYs that were registered during probe on probe errors and on driver
unbind. It also fixes a related of-node leak on late probe errors.

This series depends on the of_phy_deregister_fixed_link() helper that
was just merged to net.

As mentioned earlier, one staging driver also suffers from a similar
leak and can be fixed up once the above mentioned helper hits mainline.

Note that these patches have only been compile tested.
====================

Acked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 8 жил өмнө
parent
commit
d262fd12cd

+ 15 - 2
drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c

@@ -50,10 +50,23 @@ static int dwmac_generic_probe(struct platform_device *pdev)
 	if (plat_dat->init) {
 	if (plat_dat->init) {
 		ret = plat_dat->init(pdev, plat_dat->bsp_priv);
 		ret = plat_dat->init(pdev, plat_dat->bsp_priv);
 		if (ret)
 		if (ret)
-			return ret;
+			goto err_remove_config_dt;
 	}
 	}
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_exit;
+
+	return 0;
+
+err_exit:
+	if (plat_dat->exit)
+		plat_dat->exit(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+	if (pdev->dev.of_node)
+		stmmac_remove_config_dt(pdev, plat_dat);
+
+	return ret;
 }
 }
 
 
 static const struct of_device_id dwmac_generic_match[] = {
 static const struct of_device_id dwmac_generic_match[] = {

+ 19 - 6
drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c

@@ -271,15 +271,17 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
 	gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
-	if (!gmac)
-		return -ENOMEM;
+	if (!gmac) {
+		err = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	gmac->pdev = pdev;
 	gmac->pdev = pdev;
 
 
 	err = ipq806x_gmac_of_parse(gmac);
 	err = ipq806x_gmac_of_parse(gmac);
 	if (err) {
 	if (err) {
 		dev_err(dev, "device tree parsing error\n");
 		dev_err(dev, "device tree parsing error\n");
-		return err;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
 	regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
@@ -300,7 +302,8 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
 	default:
 	default:
 		dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
 		dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
 			phy_modes(gmac->phy_mode));
 			phy_modes(gmac->phy_mode));
-		return -EINVAL;
+		err = -EINVAL;
+		goto err_remove_config_dt;
 	}
 	}
 	regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
 	regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
 
 
@@ -319,7 +322,8 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
 	default:
 	default:
 		dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
 		dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
 			phy_modes(gmac->phy_mode));
 			phy_modes(gmac->phy_mode));
-		return -EINVAL;
+		err = -EINVAL;
+		goto err_remove_config_dt;
 	}
 	}
 	regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
 	regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
 
 
@@ -346,7 +350,16 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
 	plat_dat->bsp_priv = gmac;
 	plat_dat->bsp_priv = gmac;
 	plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed;
 	plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed;
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (err)
+		goto err_remove_config_dt;
+
+	return 0;
+
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
+
+	return err;
 }
 }
 
 
 static const struct of_device_id ipq806x_gmac_dwmac_match[] = {
 static const struct of_device_id ipq806x_gmac_dwmac_match[] = {

+ 14 - 3
drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c

@@ -46,7 +46,8 @@ static int lpc18xx_dwmac_probe(struct platform_device *pdev)
 	reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
 	reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
 	if (IS_ERR(reg)) {
 	if (IS_ERR(reg)) {
 		dev_err(&pdev->dev, "syscon lookup failed\n");
 		dev_err(&pdev->dev, "syscon lookup failed\n");
-		return PTR_ERR(reg);
+		ret = PTR_ERR(reg);
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	if (plat_dat->interface == PHY_INTERFACE_MODE_MII) {
 	if (plat_dat->interface == PHY_INTERFACE_MODE_MII) {
@@ -55,13 +56,23 @@ static int lpc18xx_dwmac_probe(struct platform_device *pdev)
 		ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII;
 		ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII;
 	} else {
 	} else {
 		dev_err(&pdev->dev, "Only MII and RMII mode supported\n");
 		dev_err(&pdev->dev, "Only MII and RMII mode supported\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	regmap_update_bits(reg, LPC18XX_CREG_CREG6,
 	regmap_update_bits(reg, LPC18XX_CREG_CREG6,
 			   LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode);
 			   LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode);
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_remove_config_dt;
+
+	return 0;
+
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
+
+	return ret;
 }
 }
 
 
 static const struct of_device_id lpc18xx_dwmac_match[] = {
 static const struct of_device_id lpc18xx_dwmac_match[] = {

+ 18 - 5
drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c

@@ -64,18 +64,31 @@ static int meson6_dwmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
-	if (!dwmac)
-		return -ENOMEM;
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	dwmac->reg = devm_ioremap_resource(&pdev->dev, res);
 	dwmac->reg = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(dwmac->reg))
-		return PTR_ERR(dwmac->reg);
+	if (IS_ERR(dwmac->reg)) {
+		ret = PTR_ERR(dwmac->reg);
+		goto err_remove_config_dt;
+	}
 
 
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed;
 	plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed;
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_remove_config_dt;
+
+	return 0;
+
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
+
+	return ret;
 }
 }
 
 
 static const struct of_device_id meson6_dwmac_match[] = {
 static const struct of_device_id meson6_dwmac_match[] = {

+ 24 - 8
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c

@@ -264,32 +264,48 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
-	if (!dwmac)
-		return -ENOMEM;
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
 	dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(dwmac->regs))
-		return PTR_ERR(dwmac->regs);
+	if (IS_ERR(dwmac->regs)) {
+		ret = PTR_ERR(dwmac->regs);
+		goto err_remove_config_dt;
+	}
 
 
 	dwmac->pdev = pdev;
 	dwmac->pdev = pdev;
 	dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
 	dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
 	if (dwmac->phy_mode < 0) {
 	if (dwmac->phy_mode < 0) {
 		dev_err(&pdev->dev, "missing phy-mode property\n");
 		dev_err(&pdev->dev, "missing phy-mode property\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	ret = meson8b_init_clk(dwmac);
 	ret = meson8b_init_clk(dwmac);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
 
 
 	ret = meson8b_init_prg_eth(dwmac);
 	ret = meson8b_init_prg_eth(dwmac);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
 
 
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->bsp_priv = dwmac;
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_clk_disable;
+
+	return 0;
+
+err_clk_disable:
+	clk_disable_unprepare(dwmac->m25_div_clk);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
+
+	return ret;
 }
 }
 
 
 static int meson8b_dwmac_remove(struct platform_device *pdev)
 static int meson8b_dwmac_remove(struct platform_device *pdev)

+ 17 - 4
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c

@@ -981,14 +981,27 @@ static int rk_gmac_probe(struct platform_device *pdev)
 	plat_dat->resume = rk_gmac_resume;
 	plat_dat->resume = rk_gmac_resume;
 
 
 	plat_dat->bsp_priv = rk_gmac_setup(pdev, data);
 	plat_dat->bsp_priv = rk_gmac_setup(pdev, data);
-	if (IS_ERR(plat_dat->bsp_priv))
-		return PTR_ERR(plat_dat->bsp_priv);
+	if (IS_ERR(plat_dat->bsp_priv)) {
+		ret = PTR_ERR(plat_dat->bsp_priv);
+		goto err_remove_config_dt;
+	}
 
 
 	ret = rk_gmac_init(pdev, plat_dat->bsp_priv);
 	ret = rk_gmac_init(pdev, plat_dat->bsp_priv);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
+
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_gmac_exit;
+
+	return 0;
+
+err_gmac_exit:
+	rk_gmac_exit(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	return ret;
 }
 }
 
 
 static const struct of_device_id rk_gmac_dwmac_match[] = {
 static const struct of_device_id rk_gmac_dwmac_match[] = {

+ 26 - 13
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c

@@ -304,6 +304,8 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
 	struct device		*dev = &pdev->dev;
 	struct device		*dev = &pdev->dev;
 	int			ret;
 	int			ret;
 	struct socfpga_dwmac	*dwmac;
 	struct socfpga_dwmac	*dwmac;
+	struct net_device	*ndev;
+	struct stmmac_priv	*stpriv;
 
 
 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
 	if (ret)
 	if (ret)
@@ -314,32 +316,43 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
 	dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
-	if (!dwmac)
-		return -ENOMEM;
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	ret = socfpga_dwmac_parse_data(dwmac, dev);
 	ret = socfpga_dwmac_parse_data(dwmac, dev);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "Unable to parse OF data\n");
 		dev_err(dev, "Unable to parse OF data\n");
-		return ret;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
 	plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
 
 
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_remove_config_dt;
 
 
-	if (!ret) {
-		struct net_device *ndev = platform_get_drvdata(pdev);
-		struct stmmac_priv *stpriv = netdev_priv(ndev);
+	ndev = platform_get_drvdata(pdev);
+	stpriv = netdev_priv(ndev);
 
 
-		/* The socfpga driver needs to control the stmmac reset to
-		 * set the phy mode. Create a copy of the core reset handel
-		 * so it can be used by the driver later.
-		 */
-		dwmac->stmmac_rst = stpriv->stmmac_rst;
+	/* The socfpga driver needs to control the stmmac reset to set the phy
+	 * mode. Create a copy of the core reset handle so it can be used by
+	 * the driver later.
+	 */
+	dwmac->stmmac_rst = stpriv->stmmac_rst;
 
 
-		ret = socfpga_dwmac_set_phy_mode(dwmac);
-	}
+	ret = socfpga_dwmac_set_phy_mode(dwmac);
+	if (ret)
+		goto err_dvr_remove;
+
+	return 0;
+
+err_dvr_remove:
+	stmmac_dvr_remove(&pdev->dev);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
 
 
 	return ret;
 	return ret;
 }
 }

+ 18 - 5
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c

@@ -345,13 +345,15 @@ static int sti_dwmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
-	if (!dwmac)
-		return -ENOMEM;
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	ret = sti_dwmac_parse_data(dwmac, pdev);
 	ret = sti_dwmac_parse_data(dwmac, pdev);
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "Unable to parse OF data\n");
 		dev_err(&pdev->dev, "Unable to parse OF data\n");
-		return ret;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	dwmac->fix_retime_src = data->fix_retime_src;
 	dwmac->fix_retime_src = data->fix_retime_src;
@@ -363,9 +365,20 @@ static int sti_dwmac_probe(struct platform_device *pdev)
 
 
 	ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
 	ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
+
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_dwmac_exit;
+
+	return 0;
+
+err_dwmac_exit:
+	sti_dwmac_exit(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
 
 
-	return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	return ret;
 }
 }
 
 
 static const struct sti_dwmac_of_data stih4xx_dwmac_data = {
 static const struct sti_dwmac_of_data stih4xx_dwmac_data = {

+ 14 - 5
drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c

@@ -107,24 +107,33 @@ static int stm32_dwmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
-	if (!dwmac)
-		return -ENOMEM;
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	ret = stm32_dwmac_parse_data(dwmac, &pdev->dev);
 	ret = stm32_dwmac_parse_data(dwmac, &pdev->dev);
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "Unable to parse OF data\n");
 		dev_err(&pdev->dev, "Unable to parse OF data\n");
-		return ret;
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	plat_dat->bsp_priv = dwmac;
 	plat_dat->bsp_priv = dwmac;
 
 
 	ret = stm32_dwmac_init(plat_dat);
 	ret = stm32_dwmac_init(plat_dat);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
 
 
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 	if (ret)
 	if (ret)
-		stm32_dwmac_clk_disable(dwmac);
+		goto err_clk_disable;
+
+	return 0;
+
+err_clk_disable:
+	stm32_dwmac_clk_disable(dwmac);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
 
 
 	return ret;
 	return ret;
 }
 }

+ 19 - 7
drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c

@@ -120,22 +120,27 @@ static int sun7i_gmac_probe(struct platform_device *pdev)
 		return PTR_ERR(plat_dat);
 		return PTR_ERR(plat_dat);
 
 
 	gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
 	gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
-	if (!gmac)
-		return -ENOMEM;
+	if (!gmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
 
 
 	gmac->interface = of_get_phy_mode(dev->of_node);
 	gmac->interface = of_get_phy_mode(dev->of_node);
 
 
 	gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
 	gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
 	if (IS_ERR(gmac->tx_clk)) {
 	if (IS_ERR(gmac->tx_clk)) {
 		dev_err(dev, "could not get tx clock\n");
 		dev_err(dev, "could not get tx clock\n");
-		return PTR_ERR(gmac->tx_clk);
+		ret = PTR_ERR(gmac->tx_clk);
+		goto err_remove_config_dt;
 	}
 	}
 
 
 	/* Optional regulator for PHY */
 	/* Optional regulator for PHY */
 	gmac->regulator = devm_regulator_get_optional(dev, "phy");
 	gmac->regulator = devm_regulator_get_optional(dev, "phy");
 	if (IS_ERR(gmac->regulator)) {
 	if (IS_ERR(gmac->regulator)) {
-		if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
+		if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) {
+			ret = -EPROBE_DEFER;
+			goto err_remove_config_dt;
+		}
 		dev_info(dev, "no regulator found\n");
 		dev_info(dev, "no regulator found\n");
 		gmac->regulator = NULL;
 		gmac->regulator = NULL;
 	}
 	}
@@ -151,11 +156,18 @@ static int sun7i_gmac_probe(struct platform_device *pdev)
 
 
 	ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
 	ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err_remove_config_dt;
 
 
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 	if (ret)
 	if (ret)
-		sun7i_gmac_exit(pdev, plat_dat->bsp_priv);
+		goto err_gmac_exit;
+
+	return 0;
+
+err_gmac_exit:
+	sun7i_gmac_exit(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
 
 
 	return ret;
 	return ret;
 }
 }

+ 0 - 1
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

@@ -3416,7 +3416,6 @@ int stmmac_dvr_remove(struct device *dev)
 	stmmac_set_mac(priv->ioaddr, false);
 	stmmac_set_mac(priv->ioaddr, false);
 	netif_carrier_off(ndev);
 	netif_carrier_off(ndev);
 	unregister_netdev(ndev);
 	unregister_netdev(ndev);
-	of_node_put(priv->plat->phy_node);
 	if (priv->stmmac_rst)
 	if (priv->stmmac_rst)
 		reset_control_assert(priv->stmmac_rst);
 		reset_control_assert(priv->stmmac_rst);
 	clk_disable_unprepare(priv->pclk);
 	clk_disable_unprepare(priv->pclk);

+ 29 - 4
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c

@@ -200,7 +200,6 @@ static int stmmac_dt_phy(struct plat_stmmacenet_data *plat,
 /**
 /**
  * stmmac_probe_config_dt - parse device-tree driver parameters
  * stmmac_probe_config_dt - parse device-tree driver parameters
  * @pdev: platform_device structure
  * @pdev: platform_device structure
- * @plat: driver data platform structure
  * @mac: MAC address to use
  * @mac: MAC address to use
  * Description:
  * Description:
  * this function is to read the driver parameters from device-tree and
  * this function is to read the driver parameters from device-tree and
@@ -306,7 +305,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 		dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
 		dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
 				       GFP_KERNEL);
 				       GFP_KERNEL);
 		if (!dma_cfg) {
 		if (!dma_cfg) {
-			of_node_put(plat->phy_node);
+			stmmac_remove_config_dt(pdev, plat);
 			return ERR_PTR(-ENOMEM);
 			return ERR_PTR(-ENOMEM);
 		}
 		}
 		plat->dma_cfg = dma_cfg;
 		plat->dma_cfg = dma_cfg;
@@ -329,14 +328,37 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 
 
 	return plat;
 	return plat;
 }
 }
+
+/**
+ * stmmac_remove_config_dt - undo the effects of stmmac_probe_config_dt()
+ * @pdev: platform_device structure
+ * @plat: driver data platform structure
+ *
+ * Release resources claimed by stmmac_probe_config_dt().
+ */
+void stmmac_remove_config_dt(struct platform_device *pdev,
+			     struct plat_stmmacenet_data *plat)
+{
+	struct device_node *np = pdev->dev.of_node;
+
+	if (of_phy_is_fixed_link(np))
+		of_phy_deregister_fixed_link(np);
+	of_node_put(plat->phy_node);
+}
 #else
 #else
 struct plat_stmmacenet_data *
 struct plat_stmmacenet_data *
 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 {
 {
 	return ERR_PTR(-ENOSYS);
 	return ERR_PTR(-ENOSYS);
 }
 }
+
+void stmmac_remove_config_dt(struct platform_device *pdev,
+			     struct plat_stmmacenet_data *plat)
+{
+}
 #endif /* CONFIG_OF */
 #endif /* CONFIG_OF */
 EXPORT_SYMBOL_GPL(stmmac_probe_config_dt);
 EXPORT_SYMBOL_GPL(stmmac_probe_config_dt);
+EXPORT_SYMBOL_GPL(stmmac_remove_config_dt);
 
 
 int stmmac_get_platform_resources(struct platform_device *pdev,
 int stmmac_get_platform_resources(struct platform_device *pdev,
 				  struct stmmac_resources *stmmac_res)
 				  struct stmmac_resources *stmmac_res)
@@ -392,10 +414,13 @@ int stmmac_pltfr_remove(struct platform_device *pdev)
 {
 {
 	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct stmmac_priv *priv = netdev_priv(ndev);
 	struct stmmac_priv *priv = netdev_priv(ndev);
+	struct plat_stmmacenet_data *plat = priv->plat;
 	int ret = stmmac_dvr_remove(&pdev->dev);
 	int ret = stmmac_dvr_remove(&pdev->dev);
 
 
-	if (priv->plat->exit)
-		priv->plat->exit(pdev, priv->plat->bsp_priv);
+	if (plat->exit)
+		plat->exit(pdev, plat->bsp_priv);
+
+	stmmac_remove_config_dt(pdev, plat);
 
 
 	return ret;
 	return ret;
 }
 }

+ 2 - 0
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h

@@ -23,6 +23,8 @@
 
 
 struct plat_stmmacenet_data *
 struct plat_stmmacenet_data *
 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac);
 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac);
+void stmmac_remove_config_dt(struct platform_device *pdev,
+			     struct plat_stmmacenet_data *plat);
 
 
 int stmmac_get_platform_resources(struct platform_device *pdev,
 int stmmac_get_platform_resources(struct platform_device *pdev,
 				  struct stmmac_resources *stmmac_res);
 				  struct stmmac_resources *stmmac_res);