507 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			507 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 634db83b82658f4641d8026e340c6027cf74a6bb Mon Sep 17 00:00:00 2001
 | |
| From: Corentin Labbe <clabbe.montjoie@gmail.com>
 | |
| Date: Tue, 24 Oct 2017 19:57:13 +0200
 | |
| Subject: [PATCH] net: stmmac: dwmac-sun8i: Handle integrated/external MDIOs
 | |
| 
 | |
| The Allwinner H3 SoC have two distinct MDIO bus, only one could be
 | |
| active at the same time.
 | |
| The selection of the active MDIO bus are done via some bits in the EMAC
 | |
| register of the system controller.
 | |
| 
 | |
| This patch implement this MDIO switch via a custom MDIO-mux.
 | |
| 
 | |
| Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
 | |
| Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | |
| Signed-off-by: David S. Miller <davem@davemloft.net>
 | |
| ---
 | |
|  drivers/net/ethernet/stmicro/stmmac/Kconfig       |   1 +
 | |
|  drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 353 ++++++++++++++--------
 | |
|  2 files changed, 224 insertions(+), 130 deletions(-)
 | |
| 
 | |
| --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
 | |
| +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
 | |
| @@ -159,6 +159,7 @@ config DWMAC_SUN8I
 | |
|  	tristate "Allwinner sun8i GMAC support"
 | |
|  	default ARCH_SUNXI
 | |
|  	depends on OF && (ARCH_SUNXI || COMPILE_TEST)
 | |
| +	select MDIO_BUS_MUX
 | |
|  	---help---
 | |
|  	  Support for Allwinner H3 A83T A64 EMAC ethernet controllers.
 | |
|  
 | |
| --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
 | |
| +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
 | |
| @@ -17,6 +17,7 @@
 | |
|  #include <linux/clk.h>
 | |
|  #include <linux/io.h>
 | |
|  #include <linux/iopoll.h>
 | |
| +#include <linux/mdio-mux.h>
 | |
|  #include <linux/mfd/syscon.h>
 | |
|  #include <linux/module.h>
 | |
|  #include <linux/of_device.h>
 | |
| @@ -41,14 +42,14 @@
 | |
|   *				This value is used for disabling properly EMAC
 | |
|   *				and used as a good starting value in case of the
 | |
|   *				boot process(uboot) leave some stuff.
 | |
| - * @internal_phy:		Does the MAC embed an internal PHY
 | |
| + * @soc_has_internal_phy:	Does the MAC embed an internal PHY
 | |
|   * @support_mii:		Does the MAC handle MII
 | |
|   * @support_rmii:		Does the MAC handle RMII
 | |
|   * @support_rgmii:		Does the MAC handle RGMII
 | |
|   */
 | |
|  struct emac_variant {
 | |
|  	u32 default_syscon_value;
 | |
| -	int internal_phy;
 | |
| +	bool soc_has_internal_phy;
 | |
|  	bool support_mii;
 | |
|  	bool support_rmii;
 | |
|  	bool support_rgmii;
 | |
| @@ -61,7 +62,8 @@ struct emac_variant {
 | |
|   * @rst_ephy:	reference to the optional EPHY reset for the internal PHY
 | |
|   * @variant:	reference to the current board variant
 | |
|   * @regmap:	regmap for using the syscon
 | |
| - * @use_internal_phy: Does the current PHY choice imply using the internal PHY
 | |
| + * @internal_phy_powered: Does the internal PHY is enabled
 | |
| + * @mux_handle:	Internal pointer used by mdio-mux lib
 | |
|   */
 | |
|  struct sunxi_priv_data {
 | |
|  	struct clk *tx_clk;
 | |
| @@ -70,12 +72,13 @@ struct sunxi_priv_data {
 | |
|  	struct reset_control *rst_ephy;
 | |
|  	const struct emac_variant *variant;
 | |
|  	struct regmap *regmap;
 | |
| -	bool use_internal_phy;
 | |
| +	bool internal_phy_powered;
 | |
| +	void *mux_handle;
 | |
|  };
 | |
|  
 | |
|  static const struct emac_variant emac_variant_h3 = {
 | |
|  	.default_syscon_value = 0x58000,
 | |
| -	.internal_phy = PHY_INTERFACE_MODE_MII,
 | |
| +	.soc_has_internal_phy = true,
 | |
|  	.support_mii = true,
 | |
|  	.support_rmii = true,
 | |
|  	.support_rgmii = true
 | |
| @@ -83,20 +86,20 @@ static const struct emac_variant emac_va
 | |
|  
 | |
|  static const struct emac_variant emac_variant_v3s = {
 | |
|  	.default_syscon_value = 0x38000,
 | |
| -	.internal_phy = PHY_INTERFACE_MODE_MII,
 | |
| +	.soc_has_internal_phy = true,
 | |
|  	.support_mii = true
 | |
|  };
 | |
|  
 | |
|  static const struct emac_variant emac_variant_a83t = {
 | |
|  	.default_syscon_value = 0,
 | |
| -	.internal_phy = 0,
 | |
| +	.soc_has_internal_phy = false,
 | |
|  	.support_mii = true,
 | |
|  	.support_rgmii = true
 | |
|  };
 | |
|  
 | |
|  static const struct emac_variant emac_variant_a64 = {
 | |
|  	.default_syscon_value = 0,
 | |
| -	.internal_phy = 0,
 | |
| +	.soc_has_internal_phy = false,
 | |
|  	.support_mii = true,
 | |
|  	.support_rmii = true,
 | |
|  	.support_rgmii = true
 | |
| @@ -195,6 +198,9 @@ static const struct emac_variant emac_va
 | |
|  #define H3_EPHY_LED_POL		BIT(17) /* 1: active low, 0: active high */
 | |
|  #define H3_EPHY_SHUTDOWN	BIT(16) /* 1: shutdown, 0: power up */
 | |
|  #define H3_EPHY_SELECT		BIT(15) /* 1: internal PHY, 0: external PHY */
 | |
| +#define H3_EPHY_MUX_MASK	(H3_EPHY_SHUTDOWN | H3_EPHY_SELECT)
 | |
| +#define DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID	1
 | |
| +#define DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID	2
 | |
|  
 | |
|  /* H3/A64 specific bits */
 | |
|  #define SYSCON_RMII_EN		BIT(13) /* 1: enable RMII (overrides EPIT) */
 | |
| @@ -635,6 +641,159 @@ static int sun8i_dwmac_reset(struct stmm
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +/* Search in mdio-mux node for internal PHY node and get its clk/reset */
 | |
| +static int get_ephy_nodes(struct stmmac_priv *priv)
 | |
| +{
 | |
| +	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| +	struct device_node *mdio_mux, *iphynode;
 | |
| +	struct device_node *mdio_internal;
 | |
| +	int ret;
 | |
| +
 | |
| +	mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux");
 | |
| +	if (!mdio_mux) {
 | |
| +		dev_err(priv->device, "Cannot get mdio-mux node\n");
 | |
| +		return -ENODEV;
 | |
| +	}
 | |
| +
 | |
| +	mdio_internal = of_find_compatible_node(mdio_mux, NULL,
 | |
| +						"allwinner,sun8i-h3-mdio-internal");
 | |
| +	if (!mdio_internal) {
 | |
| +		dev_err(priv->device, "Cannot get internal_mdio node\n");
 | |
| +		return -ENODEV;
 | |
| +	}
 | |
| +
 | |
| +	/* Seek for internal PHY */
 | |
| +	for_each_child_of_node(mdio_internal, iphynode) {
 | |
| +		gmac->ephy_clk = of_clk_get(iphynode, 0);
 | |
| +		if (IS_ERR(gmac->ephy_clk))
 | |
| +			continue;
 | |
| +		gmac->rst_ephy = of_reset_control_get_exclusive(iphynode, NULL);
 | |
| +		if (IS_ERR(gmac->rst_ephy)) {
 | |
| +			ret = PTR_ERR(gmac->rst_ephy);
 | |
| +			if (ret == -EPROBE_DEFER)
 | |
| +				return ret;
 | |
| +			continue;
 | |
| +		}
 | |
| +		dev_info(priv->device, "Found internal PHY node\n");
 | |
| +		return 0;
 | |
| +	}
 | |
| +	return -ENODEV;
 | |
| +}
 | |
| +
 | |
| +static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv)
 | |
| +{
 | |
| +	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| +	int ret;
 | |
| +
 | |
| +	if (gmac->internal_phy_powered) {
 | |
| +		dev_warn(priv->device, "Internal PHY already powered\n");
 | |
| +		return 0;
 | |
| +	}
 | |
| +
 | |
| +	dev_info(priv->device, "Powering internal PHY\n");
 | |
| +	ret = clk_prepare_enable(gmac->ephy_clk);
 | |
| +	if (ret) {
 | |
| +		dev_err(priv->device, "Cannot enable internal PHY\n");
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	/* Make sure the EPHY is properly reseted, as U-Boot may leave
 | |
| +	 * it at deasserted state, and thus it may fail to reset EMAC.
 | |
| +	 */
 | |
| +	reset_control_assert(gmac->rst_ephy);
 | |
| +
 | |
| +	ret = reset_control_deassert(gmac->rst_ephy);
 | |
| +	if (ret) {
 | |
| +		dev_err(priv->device, "Cannot deassert internal phy\n");
 | |
| +		clk_disable_unprepare(gmac->ephy_clk);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	gmac->internal_phy_powered = true;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
 | |
| +{
 | |
| +	if (!gmac->internal_phy_powered)
 | |
| +		return 0;
 | |
| +
 | |
| +	clk_disable_unprepare(gmac->ephy_clk);
 | |
| +	reset_control_assert(gmac->rst_ephy);
 | |
| +	gmac->internal_phy_powered = false;
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +/* MDIO multiplexing switch function
 | |
| + * This function is called by the mdio-mux layer when it thinks the mdio bus
 | |
| + * multiplexer needs to switch.
 | |
| + * 'current_child' is the current value of the mux register
 | |
| + * 'desired_child' is the value of the 'reg' property of the target child MDIO
 | |
| + * node.
 | |
| + * The first time this function is called, current_child == -1.
 | |
| + * If current_child == desired_child, then the mux is already set to the
 | |
| + * correct bus.
 | |
| + */
 | |
| +static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
 | |
| +				     void *data)
 | |
| +{
 | |
| +	struct stmmac_priv *priv = data;
 | |
| +	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| +	u32 reg, val;
 | |
| +	int ret = 0;
 | |
| +	bool need_power_ephy = false;
 | |
| +
 | |
| +	if (current_child ^ desired_child) {
 | |
| +		regmap_read(gmac->regmap, SYSCON_EMAC_REG, ®);
 | |
| +		switch (desired_child) {
 | |
| +		case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID:
 | |
| +			dev_info(priv->device, "Switch mux to internal PHY");
 | |
| +			val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SELECT;
 | |
| +
 | |
| +			need_power_ephy = true;
 | |
| +			break;
 | |
| +		case DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID:
 | |
| +			dev_info(priv->device, "Switch mux to external PHY");
 | |
| +			val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SHUTDOWN;
 | |
| +			need_power_ephy = false;
 | |
| +			break;
 | |
| +		default:
 | |
| +			dev_err(priv->device, "Invalid child ID %x\n",
 | |
| +				desired_child);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +		regmap_write(gmac->regmap, SYSCON_EMAC_REG, val);
 | |
| +		if (need_power_ephy) {
 | |
| +			ret = sun8i_dwmac_power_internal_phy(priv);
 | |
| +			if (ret)
 | |
| +				return ret;
 | |
| +		} else {
 | |
| +			sun8i_dwmac_unpower_internal_phy(gmac);
 | |
| +		}
 | |
| +		/* After changing syscon value, the MAC need reset or it will
 | |
| +		 * use the last value (and so the last PHY set).
 | |
| +		 */
 | |
| +		ret = sun8i_dwmac_reset(priv);
 | |
| +	}
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int sun8i_dwmac_register_mdio_mux(struct stmmac_priv *priv)
 | |
| +{
 | |
| +	int ret;
 | |
| +	struct device_node *mdio_mux;
 | |
| +	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| +
 | |
| +	mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux");
 | |
| +	if (!mdio_mux)
 | |
| +		return -ENODEV;
 | |
| +
 | |
| +	ret = mdio_mux_init(priv->device, mdio_mux, mdio_mux_syscon_switch_fn,
 | |
| +			    &gmac->mux_handle, priv, priv->mii);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
|  static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
 | |
|  {
 | |
|  	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| @@ -649,35 +808,25 @@ static int sun8i_dwmac_set_syscon(struct
 | |
|  			 "Current syscon value is not the default %x (expect %x)\n",
 | |
|  			 val, reg);
 | |
|  
 | |
| -	if (gmac->variant->internal_phy) {
 | |
| -		if (!gmac->use_internal_phy) {
 | |
| -			/* switch to external PHY interface */
 | |
| -			reg &= ~H3_EPHY_SELECT;
 | |
| -		} else {
 | |
| -			reg |= H3_EPHY_SELECT;
 | |
| -			reg &= ~H3_EPHY_SHUTDOWN;
 | |
| -			dev_dbg(priv->device, "Select internal_phy %x\n", reg);
 | |
| -
 | |
| -			if (of_property_read_bool(priv->plat->phy_node,
 | |
| -						  "allwinner,leds-active-low"))
 | |
| -				reg |= H3_EPHY_LED_POL;
 | |
| -			else
 | |
| -				reg &= ~H3_EPHY_LED_POL;
 | |
| -
 | |
| -			/* Force EPHY xtal frequency to 24MHz. */
 | |
| -			reg |= H3_EPHY_CLK_SEL;
 | |
| -
 | |
| -			ret = of_mdio_parse_addr(priv->device,
 | |
| -						 priv->plat->phy_node);
 | |
| -			if (ret < 0) {
 | |
| -				dev_err(priv->device, "Could not parse MDIO addr\n");
 | |
| -				return ret;
 | |
| -			}
 | |
| -			/* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
 | |
| -			 * address. No need to mask it again.
 | |
| -			 */
 | |
| -			reg |= ret << H3_EPHY_ADDR_SHIFT;
 | |
| +	if (gmac->variant->soc_has_internal_phy) {
 | |
| +		if (of_property_read_bool(priv->plat->phy_node,
 | |
| +					  "allwinner,leds-active-low"))
 | |
| +			reg |= H3_EPHY_LED_POL;
 | |
| +		else
 | |
| +			reg &= ~H3_EPHY_LED_POL;
 | |
| +
 | |
| +		/* Force EPHY xtal frequency to 24MHz. */
 | |
| +		reg |= H3_EPHY_CLK_SEL;
 | |
| +
 | |
| +		ret = of_mdio_parse_addr(priv->device, priv->plat->phy_node);
 | |
| +		if (ret < 0) {
 | |
| +			dev_err(priv->device, "Could not parse MDIO addr\n");
 | |
| +			return ret;
 | |
|  		}
 | |
| +		/* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
 | |
| +		 * address. No need to mask it again.
 | |
| +		 */
 | |
| +		reg |= 1 << H3_EPHY_ADDR_SHIFT;
 | |
|  	}
 | |
|  
 | |
|  	if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) {
 | |
| @@ -750,81 +899,21 @@ static void sun8i_dwmac_unset_syscon(str
 | |
|  	regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
 | |
|  }
 | |
|  
 | |
| -static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv)
 | |
| +static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
 | |
|  {
 | |
| -	struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (!gmac->use_internal_phy)
 | |
| -		return 0;
 | |
| +	struct sunxi_priv_data *gmac = priv;
 | |
|  
 | |
| -	ret = clk_prepare_enable(gmac->ephy_clk);
 | |
| -	if (ret) {
 | |
| -		dev_err(priv->device, "Cannot enable ephy\n");
 | |
| -		return ret;
 | |
| +	if (gmac->variant->soc_has_internal_phy) {
 | |
| +		/* sun8i_dwmac_exit could be called with mdiomux uninit */
 | |
| +		if (gmac->mux_handle)
 | |
| +			mdio_mux_uninit(gmac->mux_handle);
 | |
| +		if (gmac->internal_phy_powered)
 | |
| +			sun8i_dwmac_unpower_internal_phy(gmac);
 | |
|  	}
 | |
|  
 | |
| -	/* Make sure the EPHY is properly reseted, as U-Boot may leave
 | |
| -	 * it at deasserted state, and thus it may fail to reset EMAC.
 | |
| -	 */
 | |
| -	reset_control_assert(gmac->rst_ephy);
 | |
| -
 | |
| -	ret = reset_control_deassert(gmac->rst_ephy);
 | |
| -	if (ret) {
 | |
| -		dev_err(priv->device, "Cannot deassert ephy\n");
 | |
| -		clk_disable_unprepare(gmac->ephy_clk);
 | |
| -		return ret;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
 | |
| -{
 | |
| -	if (!gmac->use_internal_phy)
 | |
| -		return 0;
 | |
| -
 | |
| -	clk_disable_unprepare(gmac->ephy_clk);
 | |
| -	reset_control_assert(gmac->rst_ephy);
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -/* sun8i_power_phy() - Activate the PHY:
 | |
| - * In case of error, no need to call sun8i_unpower_phy(),
 | |
| - * it will be called anyway by sun8i_dwmac_exit()
 | |
| - */
 | |
| -static int sun8i_power_phy(struct stmmac_priv *priv)
 | |
| -{
 | |
| -	int ret;
 | |
| -
 | |
| -	ret = sun8i_dwmac_power_internal_phy(priv);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -
 | |
| -	ret = sun8i_dwmac_set_syscon(priv);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -
 | |
| -	/* After changing syscon value, the MAC need reset or it will use
 | |
| -	 * the last value (and so the last PHY set.
 | |
| -	 */
 | |
| -	ret = sun8i_dwmac_reset(priv);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static void sun8i_unpower_phy(struct sunxi_priv_data *gmac)
 | |
| -{
 | |
|  	sun8i_dwmac_unset_syscon(gmac);
 | |
| -	sun8i_dwmac_unpower_internal_phy(gmac);
 | |
| -}
 | |
| -
 | |
| -static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
 | |
| -{
 | |
| -	struct sunxi_priv_data *gmac = priv;
 | |
|  
 | |
| -	sun8i_unpower_phy(gmac);
 | |
| +	reset_control_put(gmac->rst_ephy);
 | |
|  
 | |
|  	clk_disable_unprepare(gmac->tx_clk);
 | |
|  
 | |
| @@ -853,7 +942,7 @@ static struct mac_device_info *sun8i_dwm
 | |
|  	if (!mac)
 | |
|  		return NULL;
 | |
|  
 | |
| -	ret = sun8i_power_phy(priv);
 | |
| +	ret = sun8i_dwmac_set_syscon(priv);
 | |
|  	if (ret)
 | |
|  		return NULL;
 | |
|  
 | |
| @@ -895,6 +984,8 @@ static int sun8i_dwmac_probe(struct plat
 | |
|  	struct sunxi_priv_data *gmac;
 | |
|  	struct device *dev = &pdev->dev;
 | |
|  	int ret;
 | |
| +	struct stmmac_priv *priv;
 | |
| +	struct net_device *ndev;
 | |
|  
 | |
|  	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
 | |
|  	if (ret)
 | |
| @@ -938,29 +1029,6 @@ static int sun8i_dwmac_probe(struct plat
 | |
|  	}
 | |
|  
 | |
|  	plat_dat->interface = of_get_phy_mode(dev->of_node);
 | |
| -	if (plat_dat->interface == gmac->variant->internal_phy) {
 | |
| -		dev_info(&pdev->dev, "Will use internal PHY\n");
 | |
| -		gmac->use_internal_phy = true;
 | |
| -		gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0);
 | |
| -		if (IS_ERR(gmac->ephy_clk)) {
 | |
| -			ret = PTR_ERR(gmac->ephy_clk);
 | |
| -			dev_err(&pdev->dev, "Cannot get EPHY clock: %d\n", ret);
 | |
| -			return -EINVAL;
 | |
| -		}
 | |
| -
 | |
| -		gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL);
 | |
| -		if (IS_ERR(gmac->rst_ephy)) {
 | |
| -			ret = PTR_ERR(gmac->rst_ephy);
 | |
| -			if (ret == -EPROBE_DEFER)
 | |
| -				return ret;
 | |
| -			dev_err(&pdev->dev, "No EPHY reset control found %d\n",
 | |
| -				ret);
 | |
| -			return -EINVAL;
 | |
| -		}
 | |
| -	} else {
 | |
| -		dev_info(&pdev->dev, "Will use external PHY\n");
 | |
| -		gmac->use_internal_phy = false;
 | |
| -	}
 | |
|  
 | |
|  	/* platform data specifying hardware features and callbacks.
 | |
|  	 * hardware features were copied from Allwinner drivers.
 | |
| @@ -979,9 +1047,34 @@ static int sun8i_dwmac_probe(struct plat
 | |
|  
 | |
|  	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 | |
|  	if (ret)
 | |
| -		sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
 | |
| +		goto dwmac_exit;
 | |
| +
 | |
| +	ndev = dev_get_drvdata(&pdev->dev);
 | |
| +	priv = netdev_priv(ndev);
 | |
| +	/* The mux must be registered after parent MDIO
 | |
| +	 * so after stmmac_dvr_probe()
 | |
| +	 */
 | |
| +	if (gmac->variant->soc_has_internal_phy) {
 | |
| +		ret = get_ephy_nodes(priv);
 | |
| +		if (ret)
 | |
| +			goto dwmac_exit;
 | |
| +		ret = sun8i_dwmac_register_mdio_mux(priv);
 | |
| +		if (ret) {
 | |
| +			dev_err(&pdev->dev, "Failed to register mux\n");
 | |
| +			goto dwmac_mux;
 | |
| +		}
 | |
| +	} else {
 | |
| +		ret = sun8i_dwmac_reset(priv);
 | |
| +		if (ret)
 | |
| +			goto dwmac_exit;
 | |
| +	}
 | |
|  
 | |
|  	return ret;
 | |
| +dwmac_mux:
 | |
| +	sun8i_dwmac_unset_syscon(gmac);
 | |
| +dwmac_exit:
 | |
| +	sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
 | |
| +return ret;
 | |
|  }
 | |
|  
 | |
|  static const struct of_device_id sun8i_dwmac_match[] = {
 | 
