diff options
Diffstat (limited to 'drivers/net/dsa/mt7530.c')
| -rw-r--r-- | drivers/net/dsa/mt7530.c | 266 | 
1 files changed, 197 insertions, 69 deletions
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index c48e29486b10..ecc13b57e619 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -13,7 +13,7 @@  #include <linux/of_mdio.h>  #include <linux/of_net.h>  #include <linux/of_platform.h> -#include <linux/phy.h> +#include <linux/phylink.h>  #include <linux/regmap.h>  #include <linux/regulator/consumer.h>  #include <linux/reset.h> @@ -633,63 +633,6 @@ mt7530_get_sset_count(struct dsa_switch *ds, int port, int sset)  	return ARRAY_SIZE(mt7530_mib);  } -static void mt7530_adjust_link(struct dsa_switch *ds, int port, -			       struct phy_device *phydev) -{ -	struct mt7530_priv *priv = ds->priv; - -	if (phy_is_pseudo_fixed_link(phydev)) { -		dev_dbg(priv->dev, "phy-mode for master device = %x\n", -			phydev->interface); - -		/* Setup TX circuit incluing relevant PAD and driving */ -		mt7530_pad_clk_setup(ds, phydev->interface); - -		if (priv->id == ID_MT7530) { -			/* Setup RX circuit, relevant PAD and driving on the -			 * host which must be placed after the setup on the -			 * device side is all finished. -			 */ -			mt7623_pad_clk_setup(ds); -		} -	} else { -		u16 lcl_adv = 0, rmt_adv = 0; -		u8 flowctrl; -		u32 mcr = PMCR_USERP_LINK | PMCR_FORCE_MODE; - -		switch (phydev->speed) { -		case SPEED_1000: -			mcr |= PMCR_FORCE_SPEED_1000; -			break; -		case SPEED_100: -			mcr |= PMCR_FORCE_SPEED_100; -			break; -		} - -		if (phydev->link) -			mcr |= PMCR_FORCE_LNK; - -		if (phydev->duplex) { -			mcr |= PMCR_FORCE_FDX; - -			if (phydev->pause) -				rmt_adv = LPA_PAUSE_CAP; -			if (phydev->asym_pause) -				rmt_adv |= LPA_PAUSE_ASYM; - -			lcl_adv = linkmode_adv_to_lcl_adv_t( -				phydev->advertising); -			flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); - -			if (flowctrl & FLOW_CTRL_TX) -				mcr |= PMCR_TX_FC_EN; -			if (flowctrl & FLOW_CTRL_RX) -				mcr |= PMCR_RX_FC_EN; -		} -		mt7530_write(priv, MT7530_PMCR_P(port), mcr); -	} -} -  static int  mt7530_cpu_port_enable(struct mt7530_priv *priv,  		       int port) @@ -698,9 +641,6 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv,  	mt7530_write(priv, MT7530_PVC_P(port),  		     PORT_SPEC_TAG); -	/* Setup the MAC by default for the cpu port */ -	mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK); -  	/* Disable auto learning on the cpu port */  	mt7530_set(priv, MT7530_PSC_P(port), SA_DIS); @@ -731,9 +671,6 @@ mt7530_port_enable(struct dsa_switch *ds, int port,  	mutex_lock(&priv->reg_mutex); -	/* Setup the MAC for the user port */ -	mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK); -  	/* Allow the user port gets connected to the cpu port and also  	 * restore the port matrix if the port is the member of a certain  	 * bridge. @@ -742,7 +679,7 @@ mt7530_port_enable(struct dsa_switch *ds, int port,  	priv->ports[port].enable = true;  	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,  		   priv->ports[port].pm); -	mt7530_port_set_status(priv, port, 1); +	mt7530_port_set_status(priv, port, 0);  	mutex_unlock(&priv->reg_mutex); @@ -1232,10 +1169,10 @@ static int  mt7530_setup(struct dsa_switch *ds)  {  	struct mt7530_priv *priv = ds->priv; -	int ret, i; -	u32 id, val; -	struct device_node *dn;  	struct mt7530_dummy_poll p; +	struct device_node *dn; +	u32 id, val; +	int ret, i;  	/* The parent node of master netdev which holds the common system  	 * controller also is the container for two GMACs nodes representing @@ -1305,6 +1242,8 @@ mt7530_setup(struct dsa_switch *ds)  	val |= MHWTRAP_MANUAL;  	mt7530_write(priv, MT7530_MHWTRAP, val); +	priv->p6_interface = PHY_INTERFACE_MODE_NA; +  	/* Enable and reset MIB counters */  	mt7530_mib_reset(ds); @@ -1329,6 +1268,191 @@ mt7530_setup(struct dsa_switch *ds)  	return 0;  } +static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port, +				      unsigned int mode, +				      const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 mcr_cur, mcr_new; + +	switch (port) { +	case 0: /* Internal phy */ +	case 1: +	case 2: +	case 3: +	case 4: +		if (state->interface != PHY_INTERFACE_MODE_GMII) +			return; +		break; +	/* case 5: Port 5 is not supported! */ +	case 6: /* 1st cpu port */ +		if (priv->p6_interface == state->interface) +			break; + +		if (state->interface != PHY_INTERFACE_MODE_RGMII && +		    state->interface != PHY_INTERFACE_MODE_TRGMII) +			return; + +		/* Setup TX circuit incluing relevant PAD and driving */ +		mt7530_pad_clk_setup(ds, state->interface); + +		if (priv->id == ID_MT7530) { +			/* Setup RX circuit, relevant PAD and driving on the +			 * host which must be placed after the setup on the +			 * device side is all finished. +			 */ +			mt7623_pad_clk_setup(ds); +		} + +		priv->p6_interface = state->interface; +		break; +	default: +		dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +		return; +	} + +	if (phylink_autoneg_inband(mode)) { +		dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +			__func__); +		return; +	} + +	mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); +	mcr_new = mcr_cur; +	mcr_new &= ~(PMCR_FORCE_SPEED_1000 | PMCR_FORCE_SPEED_100 | +		     PMCR_FORCE_FDX | PMCR_TX_FC_EN | PMCR_RX_FC_EN); +	mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN | +		   PMCR_BACKPR_EN | PMCR_FORCE_MODE | PMCR_FORCE_LNK; + +	switch (state->speed) { +	case SPEED_1000: +		mcr_new |= PMCR_FORCE_SPEED_1000; +		break; +	case SPEED_100: +		mcr_new |= PMCR_FORCE_SPEED_100; +		break; +	} +	if (state->duplex == DUPLEX_FULL) { +		mcr_new |= PMCR_FORCE_FDX; +		if (state->pause & MLO_PAUSE_TX) +			mcr_new |= PMCR_TX_FC_EN; +		if (state->pause & MLO_PAUSE_RX) +			mcr_new |= PMCR_RX_FC_EN; +	} + +	if (mcr_new != mcr_cur) +		mt7530_write(priv, MT7530_PMCR_P(port), mcr_new); +} + +static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port, +					 unsigned int mode, +					 phy_interface_t interface) +{ +	struct mt7530_priv *priv = ds->priv; + +	mt7530_port_set_status(priv, port, 0); +} + +static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port, +				       unsigned int mode, +				       phy_interface_t interface, +				       struct phy_device *phydev) +{ +	struct mt7530_priv *priv = ds->priv; + +	mt7530_port_set_status(priv, port, 1); +} + +static void mt7530_phylink_validate(struct dsa_switch *ds, int port, +				    unsigned long *supported, +				    struct phylink_link_state *state) +{ +	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + +	switch (port) { +	case 0: /* Internal phy */ +	case 1: +	case 2: +	case 3: +	case 4: +		if (state->interface != PHY_INTERFACE_MODE_NA && +		    state->interface != PHY_INTERFACE_MODE_GMII) +			goto unsupported; +		break; +	/* case 5: Port 5 not supported! */ +	case 6: /* 1st cpu port */ +		if (state->interface != PHY_INTERFACE_MODE_NA && +		    state->interface != PHY_INTERFACE_MODE_RGMII && +		    state->interface != PHY_INTERFACE_MODE_TRGMII) +			goto unsupported; +		break; +	default: +		dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +unsupported: +		linkmode_zero(supported); +		return; +	} + +	phylink_set_port_modes(mask); +	phylink_set(mask, Autoneg); + +	if (state->interface != PHY_INTERFACE_MODE_TRGMII) { +		phylink_set(mask, 10baseT_Half); +		phylink_set(mask, 10baseT_Full); +		phylink_set(mask, 100baseT_Half); +		phylink_set(mask, 100baseT_Full); +		phylink_set(mask, 1000baseT_Half); +	} + +	phylink_set(mask, 1000baseT_Full); + +	phylink_set(mask, Pause); +	phylink_set(mask, Asym_Pause); + +	linkmode_and(supported, supported, mask); +	linkmode_and(state->advertising, state->advertising, mask); +} + +static int +mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port, +			      struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 pmsr; + +	if (port < 0 || port >= MT7530_NUM_PORTS) +		return -EINVAL; + +	pmsr = mt7530_read(priv, MT7530_PMSR_P(port)); + +	state->link = (pmsr & PMSR_LINK); +	state->an_complete = state->link; +	state->duplex = !!(pmsr & PMSR_DPX); + +	switch (pmsr & PMSR_SPEED_MASK) { +	case PMSR_SPEED_10: +		state->speed = SPEED_10; +		break; +	case PMSR_SPEED_100: +		state->speed = SPEED_100; +		break; +	case PMSR_SPEED_1000: +		state->speed = SPEED_1000; +		break; +	default: +		state->speed = SPEED_UNKNOWN; +		break; +	} + +	state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX); +	if (pmsr & PMSR_RX_FC) +		state->pause |= MLO_PAUSE_RX; +	if (pmsr & PMSR_TX_FC) +		state->pause |= MLO_PAUSE_TX; + +	return 1; +} +  static const struct dsa_switch_ops mt7530_switch_ops = {  	.get_tag_protocol	= mtk_get_tag_protocol,  	.setup			= mt7530_setup, @@ -1337,7 +1461,6 @@ static const struct dsa_switch_ops mt7530_switch_ops = {  	.phy_write		= mt7530_phy_write,  	.get_ethtool_stats	= mt7530_get_ethtool_stats,  	.get_sset_count		= mt7530_get_sset_count, -	.adjust_link		= mt7530_adjust_link,  	.port_enable		= mt7530_port_enable,  	.port_disable		= mt7530_port_disable,  	.port_stp_state_set	= mt7530_stp_state_set, @@ -1350,6 +1473,11 @@ static const struct dsa_switch_ops mt7530_switch_ops = {  	.port_vlan_prepare	= mt7530_port_vlan_prepare,  	.port_vlan_add		= mt7530_port_vlan_add,  	.port_vlan_del		= mt7530_port_vlan_del, +	.phylink_validate	= mt7530_phylink_validate, +	.phylink_mac_link_state = mt7530_phylink_mac_link_state, +	.phylink_mac_config	= mt7530_phylink_mac_config, +	.phylink_mac_link_down	= mt7530_phylink_mac_link_down, +	.phylink_mac_link_up	= mt7530_phylink_mac_link_up,  };  static const struct of_device_id mt7530_of_match[] = {  | 
