diff options
Diffstat (limited to 'drivers/net/dsa')
38 files changed, 2691 insertions, 861 deletions
| diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 1a69286daa8d..c283593bef17 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -681,7 +681,9 @@ int b53_configure_vlan(struct dsa_switch *ds)  {  	struct b53_device *dev = ds->priv;  	struct b53_vlan vl = { 0 }; +	struct b53_vlan *v;  	int i, def_vid; +	u16 vid;  	def_vid = b53_default_pvid(dev); @@ -699,8 +701,18 @@ int b53_configure_vlan(struct dsa_switch *ds)  		b53_write16(dev, B53_VLAN_PAGE,  			    B53_VLAN_PORT_DEF_TAG(i), def_vid); -	if (!is5325(dev) && !is5365(dev)) -		b53_set_jumbo(dev, dev->enable_jumbo, false); +	/* Upon initial call we have not set-up any VLANs, but upon +	 * system resume, we need to restore all VLAN entries. +	 */ +	for (vid = def_vid; vid < dev->num_vlans; vid++) { +		v = &dev->vlans[vid]; + +		if (!v->members) +			continue; + +		b53_set_vlan_entry(dev, vid, v); +		b53_fast_age_vlan(dev, vid); +	}  	return 0;  } @@ -807,8 +819,6 @@ static int b53_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)  static int b53_reset_switch(struct b53_device *priv)  {  	/* reset vlans */ -	priv->enable_jumbo = false; -  	memset(priv->vlans, 0, sizeof(*priv->vlans) * priv->num_vlans);  	memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports); @@ -1289,7 +1299,9 @@ EXPORT_SYMBOL(b53_phylink_mac_link_down);  void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,  			     unsigned int mode,  			     phy_interface_t interface, -			     struct phy_device *phydev) +			     struct phy_device *phydev, +			     int speed, int duplex, +			     bool tx_pause, bool rx_pause)  {  	struct b53_device *dev = ds->priv; @@ -1343,6 +1355,14 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port,  	if ((is5325(dev) || is5365(dev)) && vlan->vid_begin == 0)  		return -EOPNOTSUPP; +	/* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of +	 * receiving VLAN tagged frames at all, we can still allow the port to +	 * be configured for egress untagged. +	 */ +	if (dev->chip_id == BCM7278_DEVICE_ID && port == 7 && +	    !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) +		return -EINVAL; +  	if (vlan->vid_end > dev->num_vlans)  		return -ERANGE; @@ -1454,6 +1474,10 @@ static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)  		reg |= ARLTBL_RW;  	else  		reg &= ~ARLTBL_RW; +	if (dev->vlan_enabled) +		reg &= ~ARLTBL_IVL_SVL_SELECT; +	else +		reg |= ARLTBL_IVL_SVL_SELECT;  	b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, reg);  	return b53_arl_op_wait(dev); @@ -1463,6 +1487,7 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,  			u16 vid, struct b53_arl_entry *ent, u8 *idx,  			bool is_valid)  { +	DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);  	unsigned int i;  	int ret; @@ -1470,6 +1495,8 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,  	if (ret)  		return ret; +	bitmap_zero(free_bins, dev->num_arl_entries); +  	/* Read the bins */  	for (i = 0; i < dev->num_arl_entries; i++) {  		u64 mac_vid; @@ -1481,13 +1508,24 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,  			   B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);  		b53_arl_to_entry(ent, mac_vid, fwd_entry); -		if (!(fwd_entry & ARLTBL_VALID)) +		if (!(fwd_entry & ARLTBL_VALID)) { +			set_bit(i, free_bins);  			continue; +		}  		if ((mac_vid & ARLTBL_MAC_MASK) != mac)  			continue; +		if (dev->vlan_enabled && +		    ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid) +			continue;  		*idx = i; +		return 0;  	} +	if (bitmap_weight(free_bins, dev->num_arl_entries) == 0) +		return -ENOSPC; + +	*idx = find_first_bit(free_bins, dev->num_arl_entries); +  	return -ENOENT;  } @@ -1517,10 +1555,21 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,  	if (op)  		return ret; -	/* We could not find a matching MAC, so reset to a new entry */ -	if (ret) { +	switch (ret) { +	case -ENOSPC: +		dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n", +			addr, vid); +		return is_valid ? ret : 0; +	case -ENOENT: +		/* We could not find a matching MAC, so reset to a new entry */ +		dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n", +			addr, vid, idx);  		fwd_entry = 0; -		idx = 1; +		break; +	default: +		dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n", +			addr, vid, idx); +		break;  	}  	/* For multicast address, the port is a bitmask and the validity @@ -1538,7 +1587,6 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,  		ent.is_valid = !!(ent.port);  	} -	ent.is_valid = is_valid;  	ent.vid = vid;  	ent.is_static = true;  	ent.is_age = false; @@ -1708,6 +1756,12 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)  	u16 pvlan, reg;  	unsigned int i; +	/* On 7278, port 7 which connects to the ASP should only receive +	 * traffic from matching CFP rules. +	 */ +	if (dev->chip_id == BCM7278_DEVICE_ID && port == 7) +		return -EINVAL; +  	/* Make this port leave the all VLANs join since we will have proper  	 * VLAN entries from now on  	 */ @@ -2063,6 +2117,26 @@ int b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)  }  EXPORT_SYMBOL(b53_set_mac_eee); +static int b53_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ +	struct b53_device *dev = ds->priv; +	bool enable_jumbo; +	bool allow_10_100; + +	if (is5325(dev) || is5365(dev)) +		return -EOPNOTSUPP; + +	enable_jumbo = (mtu >= JMS_MIN_SIZE); +	allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); + +	return b53_set_jumbo(dev, enable_jumbo, allow_10_100); +} + +static int b53_get_max_mtu(struct dsa_switch *ds, int port) +{ +	return JMS_MAX_SIZE; +} +  static const struct dsa_switch_ops b53_switch_ops = {  	.get_tag_protocol	= b53_get_tag_protocol,  	.setup			= b53_setup, @@ -2100,6 +2174,8 @@ static const struct dsa_switch_ops b53_switch_ops = {  	.port_mdb_prepare	= b53_mdb_prepare,  	.port_mdb_add		= b53_mdb_add,  	.port_mdb_del		= b53_mdb_del, +	.port_max_mtu		= b53_get_max_mtu, +	.port_change_mtu	= b53_change_mtu,  };  struct b53_chip_data { diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 3c30f3a7eb29..3d42318bc3f1 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -338,7 +338,9 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,  void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,  			     unsigned int mode,  			     phy_interface_t interface, -			     struct phy_device *phydev); +			     struct phy_device *phydev, +			     int speed, int duplex, +			     bool tx_pause, bool rx_pause);  int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);  int b53_vlan_prepare(struct dsa_switch *ds, int port,  		     const struct switchdev_obj_port_vlan *vlan); diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h index 2a9f421680aa..c90985c294a2 100644 --- a/drivers/net/dsa/b53/b53_regs.h +++ b/drivers/net/dsa/b53/b53_regs.h @@ -292,6 +292,7 @@  /* ARL Table Read/Write Register (8 bit) */  #define B53_ARLTBL_RW_CTRL		0x00  #define    ARLTBL_RW			BIT(0) +#define    ARLTBL_IVL_SVL_SELECT	BIT(6)  #define    ARLTBL_START_DONE		BIT(7)  /* MAC Address Index Register (48 bit) */ @@ -304,7 +305,7 @@   *   * BCM5325 and BCM5365 share most definitions below   */ -#define B53_ARLTBL_MAC_VID_ENTRY(n)	(0x10 * (n)) +#define B53_ARLTBL_MAC_VID_ENTRY(n)	((0x10 * (n)) + 0x10)  #define   ARLTBL_MAC_MASK		0xffffffffffffULL  #define   ARLTBL_VID_S			48  #define   ARLTBL_VID_MASK_25		0xff @@ -316,13 +317,16 @@  #define   ARLTBL_VALID_25		BIT(63)  /* ARL Table Data Entry N Registers (32 bit) */ -#define B53_ARLTBL_DATA_ENTRY(n)	((0x10 * (n)) + 0x08) +#define B53_ARLTBL_DATA_ENTRY(n)	((0x10 * (n)) + 0x18)  #define   ARLTBL_DATA_PORT_ID_MASK	0x1ff  #define   ARLTBL_TC(tc)			((3 & tc) << 11)  #define   ARLTBL_AGE			BIT(14)  #define   ARLTBL_STATIC			BIT(15)  #define   ARLTBL_VALID			BIT(16) +/* Maximum number of bin entries in the ARL for all switches */ +#define B53_ARLTBL_MAX_BIN_ENTRIES	4 +  /* ARL Search Control Register (8 bit) */  #define B53_ARL_SRCH_CTL		0x50  #define B53_ARL_SRCH_CTL_25		0x20 diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index b0f5280a83cb..c7ac63f41918 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -178,9 +178,17 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,  	core_writel(priv, reg, CORE_DIS_LEARN);  	/* Enable Broadcom tags for that port if requested */ -	if (priv->brcm_tag_mask & BIT(port)) +	if (priv->brcm_tag_mask & BIT(port)) {  		b53_brcm_hdr_setup(ds, port); +		/* Disable learning on ASP port */ +		if (port == 7) { +			reg = core_readl(priv, CORE_DIS_LEARN); +			reg |= BIT(port); +			core_writel(priv, reg, CORE_DIS_LEARN); +		} +	} +  	/* Configure Traffic Class to QoS mapping, allow each priority to map  	 * to a different queue number  	 */ @@ -472,7 +480,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)  	priv->slave_mii_bus->parent = ds->dev->parent;  	priv->slave_mii_bus->phy_mask = ~priv->indir_phy_mask; -	err = of_mdiobus_register(priv->slave_mii_bus, dn); +	err = mdiobus_register(priv->slave_mii_bus);  	if (err && dn)  		of_node_put(dn); @@ -648,7 +656,9 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,  static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,  				   unsigned int mode,  				   phy_interface_t interface, -				   struct phy_device *phydev) +				   struct phy_device *phydev, +				   int speed, int duplex, +				   bool tx_pause, bool rx_pause)  {  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);  	struct ethtool_eee *p = &priv->dev->ports[port].eee; @@ -1069,6 +1079,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)  	const struct bcm_sf2_of_data *data;  	struct b53_platform_data *pdata;  	struct dsa_switch_ops *ops; +	struct device_node *ports;  	struct bcm_sf2_priv *priv;  	struct b53_device *dev;  	struct dsa_switch *ds; @@ -1136,7 +1147,11 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)  	set_bit(0, priv->cfp.used);  	set_bit(0, priv->cfp.unique); -	bcm_sf2_identify_ports(priv, dn->child); +	ports = of_find_node_by_name(dn, "ports"); +	if (ports) { +		bcm_sf2_identify_ports(priv, ports); +		of_node_put(ports); +	}  	priv->irq0 = irq_of_parse_and_map(dn, 0);  	priv->irq1 = irq_of_parse_and_map(dn, 1); diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 1962c8330daa..f707edc641cf 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -13,6 +13,8 @@  #include <net/dsa.h>  #include <linux/bitmap.h>  #include <net/flow_offload.h> +#include <net/switchdev.h> +#include <uapi/linux/if_bridge.h>  #include "bcm_sf2.h"  #include "bcm_sf2_regs.h" @@ -261,16 +263,27 @@ static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,  static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,  				   struct flow_dissector_key_ipv4_addrs *addrs,  				   struct flow_dissector_key_ports *ports, -				   unsigned int slice_num, +				   const __be16 vlan_tci, +				   unsigned int slice_num, u8 num_udf,  				   bool mask)  {  	u32 reg, offset; +	/* UDF_Valid[7:0]	[31:24] +	 * S-Tag		[23:8] +	 * C-Tag		[7:0] +	 */ +	reg = udf_lower_bits(num_udf) << 24 | be16_to_cpu(vlan_tci) >> 8; +	if (mask) +		core_writel(priv, reg, CORE_CFP_MASK_PORT(5)); +	else +		core_writel(priv, reg, CORE_CFP_DATA_PORT(5)); +  	/* C-Tag		[31:24]  	 * UDF_n_A8		[23:8]  	 * UDF_n_A7		[7:0]  	 */ -	reg = 0; +	reg = (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24;  	if (mask)  		offset = CORE_CFP_MASK_PORT(4);  	else @@ -336,6 +349,7 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,  				     struct ethtool_rx_flow_spec *fs)  {  	struct ethtool_rx_flow_spec_input input = {}; +	__be16 vlan_tci = 0 , vlan_m_tci = 0xffff;  	const struct cfp_udf_layout *layout;  	unsigned int slice_num, rule_index;  	struct ethtool_rx_flow_rule *flow; @@ -360,6 +374,12 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,  	ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); +	/* Extract VLAN TCI */ +	if (fs->flow_type & FLOW_EXT) { +		vlan_tci = fs->h_ext.vlan_tci; +		vlan_m_tci = fs->m_ext.vlan_tci; +	} +  	/* Locate the first rule available */  	if (fs->location == RX_CLS_LOC_ANY)  		rule_index = find_first_zero_bit(priv->cfp.used, @@ -421,18 +441,11 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,  	core_writel(priv, layout->udfs[slice_num].mask_value |  		    udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); -	/* UDF_Valid[7:0]	[31:24] -	 * S-Tag		[23:8] -	 * C-Tag		[7:0] -	 */ -	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); - -	/* Mask all but valid UDFs */ -	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); -  	/* Program the match and the mask */ -	bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, slice_num, false); -	bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, SLICE_NUM_MASK, true); +	bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, vlan_tci, +			       slice_num, num_udf, false); +	bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, vlan_m_tci, +			       SLICE_NUM_MASK, num_udf, true);  	/* Insert into TCAM now */  	bcm_sf2_cfp_rule_addr_set(priv, rule_index); @@ -468,17 +481,29 @@ out_err_flow_rule:  static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,  				   const __be32 *ip6_addr, const __be16 port, -				   unsigned int slice_num, +				   const __be16 vlan_tci, +				   unsigned int slice_num, u32 udf_bits,  				   bool mask)  {  	u32 reg, tmp, val, offset; +	/* UDF_Valid[7:0]	[31:24] +	 * S-Tag		[23:8] +	 * C-Tag		[7:0] +	 */ +	reg = udf_bits << 24 | be16_to_cpu(vlan_tci) >> 8; +	if (mask) +		core_writel(priv, reg, CORE_CFP_MASK_PORT(5)); +	else +		core_writel(priv, reg, CORE_CFP_DATA_PORT(5)); +  	/* C-Tag		[31:24]  	 * UDF_n_B8		[23:8]	(port)  	 * UDF_n_B7 (upper)	[7:0]	(addr[15:8])  	 */  	reg = be32_to_cpu(ip6_addr[3]);  	val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); +	val |= (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24;  	if (mask)  		offset = CORE_CFP_MASK_PORT(4);  	else @@ -587,6 +612,11 @@ static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port,  		ret = memcmp(&rule->fs.h_u, &fs->h_u, fs_size);  		ret |= memcmp(&rule->fs.m_u, &fs->m_u, fs_size); +		/* Compare VLAN TCI values as well */ +		if (rule->fs.flow_type & FLOW_EXT) { +			ret |= rule->fs.h_ext.vlan_tci != fs->h_ext.vlan_tci; +			ret |= rule->fs.m_ext.vlan_tci != fs->m_ext.vlan_tci; +		}  		if (ret == 0)  			break;  	} @@ -600,6 +630,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,  				     struct ethtool_rx_flow_spec *fs)  {  	struct ethtool_rx_flow_spec_input input = {}; +	__be16 vlan_tci = 0, vlan_m_tci = 0xffff;  	unsigned int slice_num, rule_index[2];  	const struct cfp_udf_layout *layout;  	struct ethtool_rx_flow_rule *flow; @@ -623,6 +654,12 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,  	ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); +	/* Extract VLAN TCI */ +	if (fs->flow_type & FLOW_EXT) { +		vlan_tci = fs->h_ext.vlan_tci; +		vlan_m_tci = fs->m_ext.vlan_tci; +	} +  	layout = &udf_tcpip6_layout;  	slice_num = bcm_sf2_get_slice_number(layout, 0);  	if (slice_num == UDF_NUM_SLICES) @@ -704,20 +741,13 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,  	reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf);  	core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); -	/* UDF_Valid[7:0]	[31:24] -	 * S-Tag		[23:8] -	 * C-Tag		[7:0] -	 */ -	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); - -	/* Mask all but valid UDFs */ -	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); -  	/* Slice the IPv6 source address and port */  	bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32, -			       ports.key->src, slice_num, false); +			       ports.key->src, vlan_tci, slice_num, +			       udf_lower_bits(num_udf), false);  	bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32, -			       ports.mask->src, SLICE_NUM_MASK, true); +			       ports.mask->src, vlan_m_tci, SLICE_NUM_MASK, +			       udf_lower_bits(num_udf), true);  	/* Insert into TCAM now because we need to insert a second rule */  	bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); @@ -768,16 +798,12 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,  		udf_lower_bits(num_udf) << 8;  	core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); -	/* Don't care */ -	core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); - -	/* Mask all */ -	core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); -  	bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32, -			       ports.key->dst, slice_num, false); +			       ports.key->dst, 0, slice_num, +			       0, false);  	bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32, -			       ports.key->dst, SLICE_NUM_MASK, true); +			       ports.key->dst, 0, SLICE_NUM_MASK, +			       0, true);  	/* Insert into TCAM now */  	bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); @@ -823,7 +849,9 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port,  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);  	s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index;  	__u64 ring_cookie = fs->ring_cookie; +	struct switchdev_obj_port_vlan vlan;  	unsigned int queue_num, port_num; +	u16 vid;  	int ret;  	/* This rule is a Wake-on-LAN filter and we must specifically @@ -843,6 +871,34 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port,  	      dsa_is_cpu_port(ds, port_num)) ||  	    port_num >= priv->hw_params.num_ports)  		return -EINVAL; + +	/* If the rule is matching a particular VLAN, make sure that we honor +	 * the matching and have it tagged or untagged on the destination port, +	 * we do this on egress with a VLAN entry. The egress tagging attribute +	 * is expected to be provided in h_ext.data[1] bit 0. A 1 means untagged, +	 * a 0 means tagged. +	 */ +	if (fs->flow_type & FLOW_EXT) { +		/* We cannot support matching multiple VLAN IDs yet */ +		if ((be16_to_cpu(fs->m_ext.vlan_tci) & VLAN_VID_MASK) != +		    VLAN_VID_MASK) +			return -EINVAL; + +		vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; +		vlan.vid_begin = vid; +		vlan.vid_end = vid; +		if (cpu_to_be32(fs->h_ext.data[1]) & 1) +			vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED; +		else +			vlan.flags = 0; + +		ret = ds->ops->port_vlan_prepare(ds, port_num, &vlan); +		if (ret) +			return ret; + +		ds->ops->port_vlan_add(ds, port_num, &vlan); +	} +  	/*  	 * We have a small oddity where Port 6 just does not have a  	 * valid bit here (so we substract by one). @@ -878,21 +934,22 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,  	int ret = -EINVAL;  	/* Check for unsupported extensions */ -	if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || -	     fs->m_ext.data[1])) +	if (fs->flow_type & FLOW_MAC_EXT)  		return -EINVAL; -	if (fs->location != RX_CLS_LOC_ANY && fs->location >= CFP_NUM_RULES) +	if (fs->location != RX_CLS_LOC_ANY && +	    fs->location > bcm_sf2_cfp_rule_size(priv))  		return -EINVAL; +	if ((fs->flow_type & FLOW_EXT) && +	    !(ds->ops->port_vlan_prepare || ds->ops->port_vlan_add || +	      ds->ops->port_vlan_del)) +		return -EOPNOTSUPP; +  	if (fs->location != RX_CLS_LOC_ANY &&  	    test_bit(fs->location, priv->cfp.used))  		return -EBUSY; -	if (fs->location != RX_CLS_LOC_ANY && -	    fs->location > bcm_sf2_cfp_rule_size(priv)) -		return -EINVAL; -  	ret = bcm_sf2_cfp_rule_cmp(priv, port, fs);  	if (ret == 0)  		return -EEXIST; @@ -973,7 +1030,7 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, u32 loc)  	struct cfp_rule *rule;  	int ret; -	if (loc >= CFP_NUM_RULES) +	if (loc > bcm_sf2_cfp_rule_size(priv))  		return -EINVAL;  	/* Refuse deleting unused rules, and those that are not unique since diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 0369c22fe3e1..cf6fa8fede33 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1517,7 +1517,9 @@ static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port,  static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port,  				      unsigned int mode,  				      phy_interface_t interface, -				      struct phy_device *phydev) +				      struct phy_device *phydev, +				      int speed, int duplex, +				      bool tx_pause, bool rx_pause)  {  	struct gswip_priv *priv = ds->priv; diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 1d7870c6df3c..4ec6a47b7f72 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -1,5 +1,6 @@  # SPDX-License-Identifier: GPL-2.0-only  config NET_DSA_MICROCHIP_KSZ_COMMON +	select NET_DSA_TAG_KSZ  	tristate  menuconfig NET_DSA_MICROCHIP_KSZ9477 diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index d8fda4a02640..fd1d6676ae4f 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -67,7 +67,7 @@ static void port_r_cnt(struct ksz_device *dev, int port)  static void ksz_mib_read_work(struct work_struct *work)  {  	struct ksz_device *dev = container_of(work, struct ksz_device, -					      mib_read); +					      mib_read.work);  	struct ksz_port_mib *mib;  	struct ksz_port *p;  	int i; @@ -93,32 +93,24 @@ static void ksz_mib_read_work(struct work_struct *work)  		p->read = false;  		mutex_unlock(&mib->cnt_mutex);  	} -} - -static void mib_monitor(struct timer_list *t) -{ -	struct ksz_device *dev = from_timer(dev, t, mib_read_timer); -	mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval); -	schedule_work(&dev->mib_read); +	schedule_delayed_work(&dev->mib_read, dev->mib_read_interval);  }  void ksz_init_mib_timer(struct ksz_device *dev)  {  	int i; +	INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); +  	/* Read MIB counters every 30 seconds to avoid overflow. */  	dev->mib_read_interval = msecs_to_jiffies(30000); -	INIT_WORK(&dev->mib_read, ksz_mib_read_work); -	timer_setup(&dev->mib_read_timer, mib_monitor, 0); -  	for (i = 0; i < dev->mib_port_cnt; i++)  		dev->dev_ops->port_init_cnt(dev, i);  	/* Start the timer 2 seconds later. */ -	dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000); -	add_timer(&dev->mib_read_timer); +	schedule_delayed_work(&dev->mib_read, msecs_to_jiffies(2000));  }  EXPORT_SYMBOL_GPL(ksz_init_mib_timer); @@ -152,7 +144,7 @@ void ksz_adjust_link(struct dsa_switch *ds, int port,  	/* Read all MIB counters when the link is going down. */  	if (!phydev->link) {  		p->read = true; -		schedule_work(&dev->mib_read); +		schedule_delayed_work(&dev->mib_read, 0);  	}  	mutex_lock(&dev->dev_mutex);  	if (!phydev->link) @@ -477,10 +469,8 @@ EXPORT_SYMBOL(ksz_switch_register);  void ksz_switch_remove(struct ksz_device *dev)  {  	/* timer started */ -	if (dev->mib_read_timer.expires) { -		del_timer_sync(&dev->mib_read_timer); -		flush_work(&dev->mib_read); -	} +	if (dev->mib_read_interval) +		cancel_delayed_work_sync(&dev->mib_read);  	dev->dev_ops->exit(dev);  	dsa_unregister_switch(dev->ds); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index a20ebb749377..f2c9bb68fd33 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -80,8 +80,7 @@ struct ksz_device {  	struct vlan_table *vlan_cache;  	struct ksz_port *ports; -	struct timer_list mib_read_timer; -	struct work_struct mib_read; +	struct delayed_work mib_read;  	unsigned long mib_read_interval;  	u16 br_member;  	u16 member; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 022466ca1c19..5c444cd722bd 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -67,58 +67,6 @@ static const struct mt7530_mib_desc mt7530_mib[] = {  };  static int -mt7623_trgmii_write(struct mt7530_priv *priv,  u32 reg, u32 val) -{ -	int ret; - -	ret =  regmap_write(priv->ethernet, TRGMII_BASE(reg), val); -	if (ret < 0) -		dev_err(priv->dev, -			"failed to priv write register\n"); -	return ret; -} - -static u32 -mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg) -{ -	int ret; -	u32 val; - -	ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val); -	if (ret < 0) { -		dev_err(priv->dev, -			"failed to priv read register\n"); -		return ret; -	} - -	return val; -} - -static void -mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg, -		  u32 mask, u32 set) -{ -	u32 val; - -	val = mt7623_trgmii_read(priv, reg); -	val &= ~mask; -	val |= set; -	mt7623_trgmii_write(priv, reg, val); -} - -static void -mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val) -{ -	mt7623_trgmii_rmw(priv, reg, 0, val); -} - -static void -mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val) -{ -	mt7623_trgmii_rmw(priv, reg, val, 0); -} - -static int  core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)  {  	struct mii_bus *bus = priv->bus; @@ -530,27 +478,6 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)  		for (i = 0 ; i < NUM_TRGMII_CTRL; i++)  			mt7530_rmw(priv, MT7530_TRGMII_RD(i),  				   RD_TAP_MASK, RD_TAP(16)); -	else -		if (priv->id != ID_MT7621) -			mt7623_trgmii_set(priv, GSW_INTF_MODE, -					  INTF_MODE_TRGMII); - -	return 0; -} - -static int -mt7623_pad_clk_setup(struct dsa_switch *ds) -{ -	struct mt7530_priv *priv = ds->priv; -	int i; - -	for (i = 0 ; i < NUM_TRGMII_CTRL; i++) -		mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i), -				    TD_DM_DRVP(8) | TD_DM_DRVN(8)); - -	mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL); -	mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST); -  	return 0;  } @@ -563,17 +490,6 @@ mt7530_mib_reset(struct dsa_switch *ds)  	mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE);  } -static void -mt7530_port_set_status(struct mt7530_priv *priv, int port, int enable) -{ -	u32 mask = PMCR_TX_EN | PMCR_RX_EN; - -	if (enable) -		mt7530_set(priv, MT7530_PMCR_P(port), mask); -	else -		mt7530_clear(priv, MT7530_PMCR_P(port), mask); -} -  static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum)  {  	struct mt7530_priv *priv = ds->priv; @@ -750,7 +666,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, 0); +	mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);  	mutex_unlock(&priv->reg_mutex); @@ -773,7 +689,7 @@ mt7530_port_disable(struct dsa_switch *ds, int port)  	priv->ports[port].enable = false;  	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,  		   PCR_MATRIX_CLR); -	mt7530_port_set_status(priv, port, 0); +	mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);  	mutex_unlock(&priv->reg_mutex);  } @@ -857,8 +773,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)  	 */  	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,  		   MT7530_PORT_MATRIX_MODE); -	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK, -		   VLAN_ATTR(MT7530_VLAN_TRANSPARENT)); +	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK, +		   VLAN_ATTR(MT7530_VLAN_TRANSPARENT) | +		   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));  	for (i = 0; i < MT7530_NUM_PORTS; i++) {  		if (dsa_is_user_port(ds, i) && @@ -874,8 +791,8 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)  	if (all_user_ports_removed) {  		mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT),  			     PCR_MATRIX(dsa_user_ports(priv->ds))); -		mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), -			     PORT_SPEC_TAG); +		mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), PORT_SPEC_TAG +			     | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));  	}  } @@ -901,8 +818,9 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)  	/* Set the port as a user port which is to be able to recognize VID  	 * from incoming packets before fetching entry within the VLAN table.  	 */ -	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK, -		   VLAN_ATTR(MT7530_VLAN_USER)); +	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK, +		   VLAN_ATTR(MT7530_VLAN_USER) | +		   PVC_EG_TAG(MT7530_VLAN_EG_DISABLED));  }  static void @@ -1222,6 +1140,64 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,  	return 0;  } +static int mt7530_port_mirror_add(struct dsa_switch *ds, int port, +				  struct dsa_mall_mirror_tc_entry *mirror, +				  bool ingress) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 val; + +	/* Check for existent entry */ +	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) +		return -EEXIST; + +	val = mt7530_read(priv, MT7530_MFC); + +	/* MT7530 only supports one monitor port */ +	if (val & MIRROR_EN && MIRROR_PORT(val) != mirror->to_local_port) +		return -EEXIST; + +	val |= MIRROR_EN; +	val &= ~MIRROR_MASK; +	val |= mirror->to_local_port; +	mt7530_write(priv, MT7530_MFC, val); + +	val = mt7530_read(priv, MT7530_PCR_P(port)); +	if (ingress) { +		val |= PORT_RX_MIR; +		priv->mirror_rx |= BIT(port); +	} else { +		val |= PORT_TX_MIR; +		priv->mirror_tx |= BIT(port); +	} +	mt7530_write(priv, MT7530_PCR_P(port), val); + +	return 0; +} + +static void mt7530_port_mirror_del(struct dsa_switch *ds, int port, +				   struct dsa_mall_mirror_tc_entry *mirror) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 val; + +	val = mt7530_read(priv, MT7530_PCR_P(port)); +	if (mirror->ingress) { +		val &= ~PORT_RX_MIR; +		priv->mirror_rx &= ~BIT(port); +	} else { +		val &= ~PORT_TX_MIR; +		priv->mirror_tx &= ~BIT(port); +	} +	mt7530_write(priv, MT7530_PCR_P(port), val); + +	if (!priv->mirror_rx && !priv->mirror_tx) { +		val = mt7530_read(priv, MT7530_MFC); +		val &= ~MIRROR_EN; +		mt7530_write(priv, MT7530_MFC, val); +	} +} +  static enum dsa_tag_protocol  mtk_get_tag_protocol(struct dsa_switch *ds, int port,  		     enum dsa_tag_protocol mp) @@ -1256,10 +1232,6 @@ mt7530_setup(struct dsa_switch *ds)  	dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;  	if (priv->id == ID_MT7530) { -		priv->ethernet = syscon_node_to_regmap(dn); -		if (IS_ERR(priv->ethernet)) -			return PTR_ERR(priv->ethernet); -  		regulator_set_voltage(priv->core_pwr, 1000000, 1000000);  		ret = regulator_enable(priv->core_pwr);  		if (ret < 0) { @@ -1333,6 +1305,10 @@ mt7530_setup(struct dsa_switch *ds)  			mt7530_cpu_port_enable(priv, i);  		else  			mt7530_port_disable(ds, i); + +		/* Enable consistent egress tag */ +		mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK, +			   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));  	}  	/* Setup port 5 */ @@ -1356,6 +1332,9 @@ mt7530_setup(struct dsa_switch *ds)  				continue;  			phy_node = of_parse_phandle(mac_np, "phy-handle", 0); +			if (!phy_node) +				continue; +  			if (phy_node->parent == priv->dev->of_node->parent) {  				ret = of_get_phy_mode(mac_np, &interface);  				if (ret && ret != -ENODEV) @@ -1418,14 +1397,6 @@ static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,  		/* 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: @@ -1441,31 +1412,14 @@ static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,  	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_LINK_SETTINGS_MASK;  	mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN | -		   PMCR_BACKPR_EN | PMCR_FORCE_MODE | PMCR_FORCE_LNK; +		   PMCR_BACKPR_EN | PMCR_FORCE_MODE;  	/* Are we connected to external phy */  	if (port == 5 && dsa_is_user_port(ds, 5))  		mcr_new |= PMCR_EXT_PHY; -	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);  } @@ -1476,17 +1430,38 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port,  {  	struct mt7530_priv *priv = ds->priv; -	mt7530_port_set_status(priv, port, 0); +	mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);  }  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 phy_device *phydev, +				       int speed, int duplex, +				       bool tx_pause, bool rx_pause)  {  	struct mt7530_priv *priv = ds->priv; +	u32 mcr; + +	mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; + +	switch (speed) { +	case SPEED_1000: +		mcr |= PMCR_FORCE_SPEED_1000; +		break; +	case SPEED_100: +		mcr |= PMCR_FORCE_SPEED_100; +		break; +	} +	if (duplex == DUPLEX_FULL) { +		mcr |= PMCR_FORCE_FDX; +		if (tx_pause) +			mcr |= PMCR_TX_FC_EN; +		if (rx_pause) +			mcr |= PMCR_RX_FC_EN; +	} -	mt7530_port_set_status(priv, port, 1); +	mt7530_set(priv, MT7530_PMCR_P(port), mcr);  }  static void mt7530_phylink_validate(struct dsa_switch *ds, int port, @@ -1611,6 +1586,8 @@ 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, +	.port_mirror_add	= mt7530_port_mirror_add, +	.port_mirror_del	= mt7530_port_mirror_del,  	.phylink_validate	= mt7530_phylink_validate,  	.phylink_mac_link_state = mt7530_phylink_mac_link_state,  	.phylink_mac_config	= mt7530_phylink_mac_config, diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index ccb9da8cad0d..979bb6374678 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -36,6 +36,9 @@ enum {  #define  CPU_EN				BIT(7)  #define  CPU_PORT(x)			((x) << 4)  #define  CPU_MASK			(0xf << 4) +#define  MIRROR_EN			BIT(3) +#define  MIRROR_PORT(x)			((x) & 0x7) +#define  MIRROR_MASK			0x7  /* Registers for address table access */  #define MT7530_ATA1			0x74 @@ -141,6 +144,8 @@ enum mt7530_stp_state {  /* Register for port control */  #define MT7530_PCR_P(x)			(0x2004 + ((x) * 0x100)) +#define  PORT_TX_MIR			BIT(9) +#define  PORT_RX_MIR			BIT(8)  #define  PORT_VLAN(x)			((x) & 0x3)  enum mt7530_port_mode { @@ -167,9 +172,16 @@ enum mt7530_port_mode {  /* Register for port vlan control */  #define MT7530_PVC_P(x)			(0x2010 + ((x) * 0x100))  #define  PORT_SPEC_TAG			BIT(5) +#define  PVC_EG_TAG(x)			(((x) & 0x7) << 8) +#define  PVC_EG_TAG_MASK		PVC_EG_TAG(7)  #define  VLAN_ATTR(x)			(((x) & 0x3) << 6)  #define  VLAN_ATTR_MASK			VLAN_ATTR(3) +enum mt7530_vlan_port_eg_tag { +	MT7530_VLAN_EG_DISABLED = 0, +	MT7530_VLAN_EG_CONSISTENT = 1, +}; +  enum mt7530_vlan_port_attr {  	MT7530_VLAN_USER = 0,  	MT7530_VLAN_TRANSPARENT = 3, @@ -201,6 +213,10 @@ enum mt7530_vlan_port_attr {  #define  PMCR_FORCE_LNK			BIT(0)  #define  PMCR_SPEED_MASK		(PMCR_FORCE_SPEED_100 | \  					 PMCR_FORCE_SPEED_1000) +#define  PMCR_LINK_SETTINGS_MASK	(PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ +					 PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ +					 PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ +					 PMCR_FORCE_FDX | PMCR_FORCE_LNK)  #define MT7530_PMSR_P(x)		(0x3008 + (x) * 0x100)  #define  PMSR_EEE1G			BIT(7) @@ -268,7 +284,6 @@ enum mt7530_vlan_port_attr {  /* Registers for TRGMII on the both side */  #define MT7530_TRGMII_RCK_CTRL		0x7a00 -#define GSW_TRGMII_RCK_CTRL		0x300  #define  RX_RST				BIT(31)  #define  RXC_DQSISEL			BIT(30)  #define  DQSI1_TAP_MASK			(0x7f << 8) @@ -277,31 +292,24 @@ enum mt7530_vlan_port_attr {  #define  DQSI0_TAP(x)			((x) & 0x7f)  #define MT7530_TRGMII_RCK_RTT		0x7a04 -#define GSW_TRGMII_RCK_RTT		0x304  #define  DQS1_GATE			BIT(31)  #define  DQS0_GATE			BIT(30)  #define MT7530_TRGMII_RD(x)		(0x7a10 + (x) * 8) -#define GSW_TRGMII_RD(x)		(0x310 + (x) * 8)  #define  BSLIP_EN			BIT(31)  #define  EDGE_CHK			BIT(30)  #define  RD_TAP_MASK			0x7f  #define  RD_TAP(x)			((x) & 0x7f) -#define GSW_TRGMII_TXCTRL		0x340  #define MT7530_TRGMII_TXCTRL		0x7a40  #define  TRAIN_TXEN			BIT(31)  #define  TXC_INV			BIT(30)  #define  TX_RST				BIT(28)  #define MT7530_TRGMII_TD_ODT(i)		(0x7a54 + 8 * (i)) -#define GSW_TRGMII_TD_ODT(i)		(0x354 + 8 * (i))  #define  TD_DM_DRVP(x)			((x) & 0xf)  #define  TD_DM_DRVN(x)			(((x) & 0xf) << 4) -#define GSW_INTF_MODE			0x390 -#define  INTF_MODE_TRGMII		BIT(1) -  #define MT7530_TRGMII_TCK_CTRL		0x7a78  #define  TCK_TAP(x)			(((x) & 0xf) << 8) @@ -434,7 +442,6 @@ static const char *p5_intf_modes(unsigned int p5_interface)   * @ds:			The pointer to the dsa core structure   * @bus:		The bus used for the device and built-in PHY   * @rstc:		The pointer to reset control used by MCM - * @ethernet:		The regmap used for access TRGMII-based registers   * @core_pwr:		The power supplied into the core   * @io_pwr:		The power supplied into the I/O   * @reset:		The descriptor for GPIO line tied to its reset pin @@ -451,7 +458,6 @@ struct mt7530_priv {  	struct dsa_switch	*ds;  	struct mii_bus		*bus;  	struct reset_control	*rstc; -	struct regmap		*ethernet;  	struct regulator	*core_pwr;  	struct regulator	*io_pwr;  	struct gpio_desc	*reset; @@ -460,6 +466,8 @@ struct mt7530_priv {  	phy_interface_t		p6_interface;  	phy_interface_t		p5_interface;  	unsigned int		p5_intf_sel; +	u8			mirror_rx; +	u8			mirror_tx;  	struct mt7530_port	ports[MT7530_NUM_PORTS];  	/* protect among processes for registers access*/ diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig index 6435020d690d..51185e4d7d15 100644 --- a/drivers/net/dsa/mv88e6xxx/Kconfig +++ b/drivers/net/dsa/mv88e6xxx/Kconfig @@ -24,8 +24,8 @@ config NET_DSA_MV88E6XXX_PTP  	bool "PTP support for Marvell 88E6xxx"  	default n  	depends on NET_DSA_MV88E6XXX_GLOBAL2 +	depends on PTP_1588_CLOCK  	imply NETWORK_PHY_TIMESTAMPING -	imply PTP_1588_CLOCK  	help  	  Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch  	  chips that support it. diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2f993e673ec7..2b4a723c8306 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -397,32 +397,35 @@ static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip)  	mv88e6xxx_reg_unlock(chip);  } -int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, -			     int speed, int duplex, int pause, -			     phy_interface_t mode) +static int mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip, +					   int port, phy_interface_t interface)  { -	struct phylink_link_state state;  	int err; -	if (!chip->info->ops->port_set_link) -		return 0; +	if (chip->info->ops->port_set_rgmii_delay) { +		err = chip->info->ops->port_set_rgmii_delay(chip, port, +							    interface); +		if (err && err != -EOPNOTSUPP) +			return err; +	} -	if (!chip->info->ops->port_link_state) -		return 0; +	if (chip->info->ops->port_set_cmode) { +		err = chip->info->ops->port_set_cmode(chip, port, +						      interface); +		if (err && err != -EOPNOTSUPP) +			return err; +	} -	err = chip->info->ops->port_link_state(chip, port, &state); -	if (err) -		return err; +	return 0; +} -	/* Has anything actually changed? We don't expect the -	 * interface mode to change without one of the other -	 * parameters also changing -	 */ -	if (state.link == link && -	    state.speed == speed && -	    state.duplex == duplex && -	    (state.interface == mode || -	     state.interface == PHY_INTERFACE_MODE_NA)) +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, +				    int link, int speed, int duplex, int pause, +				    phy_interface_t mode) +{ +	int err; + +	if (!chip->info->ops->port_set_link)  		return 0;  	/* Port's MAC control must not be changed unless the link is down */ @@ -430,8 +433,9 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,  	if (err)  		return err; -	if (chip->info->ops->port_set_speed) { -		err = chip->info->ops->port_set_speed(chip, port, speed); +	if (chip->info->ops->port_set_speed_duplex) { +		err = chip->info->ops->port_set_speed_duplex(chip, port, +							     speed, duplex);  		if (err && err != -EOPNOTSUPP)  			goto restore_link;  	} @@ -445,25 +449,7 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,  			goto restore_link;  	} -	if (chip->info->ops->port_set_duplex) { -		err = chip->info->ops->port_set_duplex(chip, port, duplex); -		if (err && err != -EOPNOTSUPP) -			goto restore_link; -	} - -	if (chip->info->ops->port_set_rgmii_delay) { -		err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); -		if (err && err != -EOPNOTSUPP) -			goto restore_link; -	} - -	if (chip->info->ops->port_set_cmode) { -		err = chip->info->ops->port_set_cmode(chip, port, mode); -		if (err && err != -EOPNOTSUPP) -			goto restore_link; -	} - -	err = 0; +	err = mv88e6xxx_port_config_interface(chip, port, mode);  restore_link:  	if (chip->info->ops->port_set_link(chip, port, link))  		dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port); @@ -478,6 +464,97 @@ static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port)  	return port < chip->info->num_internal_phys;  } +static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); +	if (err) { +		dev_err(chip->dev, +			"p%d: %s: failed to read port status\n", +			port, __func__); +		return err; +	} + +	return !!(reg & MV88E6XXX_PORT_STS_PHY_DETECT); +} + +static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port, +					  struct phylink_link_state *state) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	u8 lane; +	int err; + +	mv88e6xxx_reg_lock(chip); +	lane = mv88e6xxx_serdes_get_lane(chip, port); +	if (lane && chip->info->ops->serdes_pcs_get_state) +		err = chip->info->ops->serdes_pcs_get_state(chip, port, lane, +							    state); +	else +		err = -EOPNOTSUPP; +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, +				       unsigned int mode, +				       phy_interface_t interface, +				       const unsigned long *advertise) +{ +	const struct mv88e6xxx_ops *ops = chip->info->ops; +	u8 lane; + +	if (ops->serdes_pcs_config) { +		lane = mv88e6xxx_serdes_get_lane(chip, port); +		if (lane) +			return ops->serdes_pcs_config(chip, port, lane, mode, +						      interface, advertise); +	} + +	return 0; +} + +static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	const struct mv88e6xxx_ops *ops; +	int err = 0; +	u8 lane; + +	ops = chip->info->ops; + +	if (ops->serdes_pcs_an_restart) { +		mv88e6xxx_reg_lock(chip); +		lane = mv88e6xxx_serdes_get_lane(chip, port); +		if (lane) +			err = ops->serdes_pcs_an_restart(chip, port, lane); +		mv88e6xxx_reg_unlock(chip); + +		if (err) +			dev_err(ds->dev, "p%d: failed to restart AN\n", port); +	} +} + +static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, +					unsigned int mode, +					int speed, int duplex) +{ +	const struct mv88e6xxx_ops *ops = chip->info->ops; +	u8 lane; + +	if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) { +		lane = mv88e6xxx_serdes_get_lane(chip, port); +		if (lane) +			return ops->serdes_pcs_link_up(chip, port, lane, +						       speed, duplex); +	} + +	return 0; +} +  static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port,  				       unsigned long *mask,  				       struct phylink_link_state *state) @@ -582,83 +659,107 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,  	phylink_helper_basex_speed(state);  } -static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, -				struct phylink_link_state *state) -{ -	struct mv88e6xxx_chip *chip = ds->priv; -	int err; - -	mv88e6xxx_reg_lock(chip); -	if (chip->info->ops->port_link_state) -		err = chip->info->ops->port_link_state(chip, port, state); -	else -		err = -EOPNOTSUPP; -	mv88e6xxx_reg_unlock(chip); - -	return err; -} -  static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,  				 unsigned int mode,  				 const struct phylink_link_state *state)  {  	struct mv88e6xxx_chip *chip = ds->priv; -	int speed, duplex, link, pause, err; +	int err; +	/* FIXME: is this the correct test? If we're in fixed mode on an +	 * internal port, why should we process this any different from +	 * PHY mode? On the other hand, the port may be automedia between +	 * an internal PHY and the serdes... +	 */  	if ((mode == MLO_AN_PHY) && mv88e6xxx_phy_is_internal(ds, port))  		return; -	if (mode == MLO_AN_FIXED) { -		link = LINK_FORCED_UP; -		speed = state->speed; -		duplex = state->duplex; -	} else if (!mv88e6xxx_phy_is_internal(ds, port)) { -		link = state->link; -		speed = state->speed; -		duplex = state->duplex; -	} else { -		speed = SPEED_UNFORCED; -		duplex = DUPLEX_UNFORCED; -		link = LINK_UNFORCED; -	} -	pause = !!phylink_test(state->advertising, Pause); -  	mv88e6xxx_reg_lock(chip); -	err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, pause, -				       state->interface); +	/* FIXME: should we force the link down here - but if we do, how +	 * do we restore the link force/unforce state? The driver layering +	 * gets in the way. +	 */ +	err = mv88e6xxx_port_config_interface(chip, port, state->interface); +	if (err && err != -EOPNOTSUPP) +		goto err_unlock; + +	err = mv88e6xxx_serdes_pcs_config(chip, port, mode, state->interface, +					  state->advertising); +	/* FIXME: we should restart negotiation if something changed - which +	 * is something we get if we convert to using phylinks PCS operations. +	 */ +	if (err > 0) +		err = 0; + +err_unlock:  	mv88e6xxx_reg_unlock(chip);  	if (err && err != -EOPNOTSUPP) -		dev_err(ds->dev, "p%d: failed to configure MAC\n", port); +		dev_err(ds->dev, "p%d: failed to configure MAC/PCS\n", port);  } -static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link) +static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, +				    unsigned int mode, +				    phy_interface_t interface)  {  	struct mv88e6xxx_chip *chip = ds->priv; -	int err; +	const struct mv88e6xxx_ops *ops; +	int err = 0; + +	ops = chip->info->ops;  	mv88e6xxx_reg_lock(chip); -	err = chip->info->ops->port_set_link(chip, port, link); +	if ((!mv88e6xxx_port_ppu_updates(chip, port) || +	     mode == MLO_AN_FIXED) && ops->port_set_link) +		err = ops->port_set_link(chip, port, LINK_FORCED_DOWN);  	mv88e6xxx_reg_unlock(chip);  	if (err) -		dev_err(chip->dev, "p%d: failed to force MAC link\n", port); -} - -static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, -				    unsigned int mode, -				    phy_interface_t interface) -{ -	if (mode == MLO_AN_FIXED) -		mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN); +		dev_err(chip->dev, +			"p%d: failed to force MAC link down\n", port);  }  static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,  				  unsigned int mode, phy_interface_t interface, -				  struct phy_device *phydev) +				  struct phy_device *phydev, +				  int speed, int duplex, +				  bool tx_pause, bool rx_pause)  { -	if (mode == MLO_AN_FIXED) -		mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP); +	struct mv88e6xxx_chip *chip = ds->priv; +	const struct mv88e6xxx_ops *ops; +	int err = 0; + +	ops = chip->info->ops; + +	mv88e6xxx_reg_lock(chip); +	if (!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) { +		/* FIXME: for an automedia port, should we force the link +		 * down here - what if the link comes up due to "other" media +		 * while we're bringing the port up, how is the exclusivity +		 * handled in the Marvell hardware? E.g. port 2 on 88E6390 +		 * shared between internal PHY and Serdes. +		 */ +		err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed, +						   duplex); +		if (err) +			goto error; + +		if (ops->port_set_speed_duplex) { +			err = ops->port_set_speed_duplex(chip, port, +							 speed, duplex); +			if (err && err != -EOPNOTSUPP) +				goto error; +		} + +		if (ops->port_set_link) +			err = ops->port_set_link(chip, port, LINK_FORCED_UP); +	} +error: +	mv88e6xxx_reg_unlock(chip); + +	if (err && err != -EOPNOTSUPP) +		dev_err(ds->dev, +			"p%d: failed to configure MAC link up\n", port);  }  static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) @@ -1018,7 +1119,14 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,  static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)  { -	return 32 * sizeof(u16); +	struct mv88e6xxx_chip *chip = ds->priv; +	int len; + +	len = 32 * sizeof(u16); +	if (chip->info->ops->serdes_get_regs_len) +		len += chip->info->ops->serdes_get_regs_len(chip, port); + +	return len;  }  static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, @@ -1043,6 +1151,9 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,  			p[i] = reg;  	} +	if (chip->info->ops->serdes_get_regs) +		chip->info->ops->serdes_get_regs(chip, port, &p[i]); +  	mv88e6xxx_reg_unlock(chip);  } @@ -1785,7 +1896,7 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid)  }  static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, -				    u16 vid, u8 member) +				    u16 vid, u8 member, bool warn)  {  	const u8 non_member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;  	struct mv88e6xxx_vtu_entry vlan; @@ -1830,7 +1941,7 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,  		err = mv88e6xxx_vtu_loadpurge(chip, &vlan);  		if (err)  			return err; -	} else { +	} else if (warn) {  		dev_info(chip->dev, "p%d: already a member of VLAN %d\n",  			 port, vid);  	} @@ -1844,6 +1955,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,  	struct mv88e6xxx_chip *chip = ds->priv;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +	bool warn;  	u8 member;  	u16 vid; @@ -1857,10 +1969,15 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,  	else  		member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED; +	/* net/dsa/slave.c will call dsa_port_vlan_add() for the affected port +	 * and then the CPU port. Do not warn for duplicates for the CPU port. +	 */ +	warn = !dsa_is_cpu_port(ds, port) && !dsa_is_dsa_port(ds, port); +  	mv88e6xxx_reg_lock(chip);  	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) -		if (mv88e6xxx_port_vlan_join(chip, port, vid, member)) +		if (mv88e6xxx_port_vlan_join(chip, port, vid, member, warn))  			dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,  				vid, untagged ? 'u' : 't'); @@ -3272,8 +3389,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {  	.phy_read = mv88e6185_phy_ppu_read,  	.phy_write = mv88e6185_phy_ppu_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3282,7 +3398,6 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3312,12 +3427,10 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {  	.phy_read = mv88e6185_phy_ppu_read,  	.phy_write = mv88e6185_phy_ppu_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_set_frame_mode = mv88e6085_port_set_frame_mode,  	.port_set_egress_floods = mv88e6185_port_set_egress_floods,  	.port_set_upstream_port = mv88e6095_port_set_upstream_port, -	.port_link_state = mv88e6185_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3343,8 +3456,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3354,7 +3466,6 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3383,13 +3494,11 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_set_frame_mode = mv88e6085_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3418,8 +3527,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {  	.phy_read = mv88e6185_phy_ppu_read,  	.phy_write = mv88e6185_phy_ppu_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6185_port_set_egress_floods, @@ -3429,7 +3537,6 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {  	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_set_pause = mv88e6185_port_set_pause, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3461,9 +3568,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6341_port_set_speed, +	.port_set_speed_duplex = mv88e6341_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6341_port_max_speed_mode,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3474,7 +3580,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6341_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3493,6 +3598,11 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6341_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status, @@ -3509,8 +3619,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3520,7 +3629,6 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3552,11 +3660,9 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {  	.phy_read = mv88e6165_phy_read,  	.phy_write = mv88e6165_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3588,9 +3694,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3600,7 +3705,6 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3632,9 +3736,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6352_port_set_speed, +	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3645,7 +3748,6 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3665,7 +3767,13 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {  	.vtu_getnext = mv88e6352_g1_vtu_getnext,  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_get_lane = mv88e6352_serdes_get_lane, +	.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6352_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,  	.serdes_power = mv88e6352_serdes_power, +	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len, +	.serdes_get_regs = mv88e6352_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.phylink_validate = mv88e6352_phylink_validate,  }; @@ -3679,9 +3787,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3691,7 +3798,6 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3723,9 +3829,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6352_port_set_speed, +	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3736,7 +3841,6 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -3756,10 +3860,16 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {  	.vtu_getnext = mv88e6352_g1_vtu_getnext,  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_get_lane = mv88e6352_serdes_get_lane, +	.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6352_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,  	.serdes_power = mv88e6352_serdes_power,  	.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6352_serdes_irq_enable,  	.serdes_irq_status = mv88e6352_serdes_irq_status, +	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len, +	.serdes_get_regs = mv88e6352_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.phylink_validate = mv88e6352_phylink_validate,  }; @@ -3772,14 +3882,12 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {  	.phy_read = mv88e6185_phy_ppu_read,  	.phy_write = mv88e6185_phy_ppu_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_set_frame_mode = mv88e6085_port_set_frame_mode,  	.port_set_egress_floods = mv88e6185_port_set_egress_floods,  	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,  	.port_set_upstream_port = mv88e6095_port_set_upstream_port,  	.port_set_pause = mv88e6185_port_set_pause, -	.port_link_state = mv88e6185_port_link_state,  	.port_get_cmode = mv88e6185_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3810,9 +3918,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390_port_set_speed, +	.port_set_speed_duplex = mv88e6390_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy, @@ -3822,7 +3929,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3844,12 +3950,18 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, -	.phylink_validate = mv88e6390_phylink_validate, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.phylink_validate = mv88e6390_phylink_validate,  }; @@ -3864,9 +3976,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390x_port_set_speed, +	.port_set_speed_duplex = mv88e6390x_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390x_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy, @@ -3876,7 +3987,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390x_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3898,12 +4008,18 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390x_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, -	.phylink_validate = mv88e6390_phylink_validate, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.phylink_validate = mv88e6390x_phylink_validate,  }; @@ -3918,9 +4034,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390_port_set_speed, +	.port_set_speed_duplex = mv88e6390_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3929,7 +4044,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -3951,12 +4065,18 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, -	.phylink_validate = mv88e6390_phylink_validate, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.avb_ops = &mv88e6390_avb_ops,  	.ptp_ops = &mv88e6352_ptp_ops,  	.phylink_validate = mv88e6390_phylink_validate, @@ -3973,9 +4093,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6352_port_set_speed, +	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -3986,7 +4105,6 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4006,10 +4124,16 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {  	.vtu_getnext = mv88e6352_g1_vtu_getnext,  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_get_lane = mv88e6352_serdes_get_lane, +	.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6352_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,  	.serdes_power = mv88e6352_serdes_power,  	.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6352_serdes_irq_enable,  	.serdes_irq_status = mv88e6352_serdes_irq_status, +	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len, +	.serdes_get_regs = mv88e6352_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.avb_ops = &mv88e6352_avb_ops,  	.ptp_ops = &mv88e6352_ptp_ops, @@ -4027,9 +4151,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6250_port_set_speed, +	.port_set_speed_duplex = mv88e6250_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4037,7 +4160,6 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {  	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6250_port_link_state,  	.stats_snapshot = mv88e6320_g1_stats_snapshot,  	.stats_set_histogram = mv88e6095_g1_stats_set_histogram,  	.stats_get_sset_count = mv88e6250_stats_get_sset_count, @@ -4066,9 +4188,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390_port_set_speed, +	.port_set_speed_duplex = mv88e6390_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy, @@ -4078,7 +4199,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4100,12 +4220,18 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, -	.phylink_validate = mv88e6390_phylink_validate, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.avb_ops = &mv88e6390_avb_ops,  	.ptp_ops = &mv88e6352_ptp_ops, @@ -4123,8 +4249,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4134,7 +4259,6 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4167,8 +4291,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4178,7 +4301,6 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4209,9 +4331,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6341_port_set_speed, +	.port_set_speed_duplex = mv88e6341_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6341_port_max_speed_mode,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4222,7 +4343,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6341_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4241,6 +4361,11 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6341_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status, @@ -4259,9 +4384,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4271,7 +4395,6 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4301,9 +4424,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_speed_duplex = mv88e6185_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode,  	.port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -4313,7 +4435,6 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4347,9 +4468,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, -	.port_set_speed = mv88e6352_port_set_speed, +	.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,  	.port_tag_remap = mv88e6095_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy,  	.port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4360,7 +4480,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port,  	.stats_snapshot = mv88e6320_g1_stats_snapshot, @@ -4380,6 +4499,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {  	.vtu_getnext = mv88e6352_g1_vtu_getnext,  	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,  	.serdes_get_lane = mv88e6352_serdes_get_lane, +	.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6352_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,  	.serdes_power = mv88e6352_serdes_power,  	.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6352_serdes_irq_enable, @@ -4390,6 +4513,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {  	.serdes_get_sset_count = mv88e6352_serdes_get_sset_count,  	.serdes_get_strings = mv88e6352_serdes_get_strings,  	.serdes_get_stats = mv88e6352_serdes_get_stats, +	.serdes_get_regs_len = mv88e6352_serdes_get_regs_len, +	.serdes_get_regs = mv88e6352_serdes_get_regs,  	.phylink_validate = mv88e6352_phylink_validate,  }; @@ -4403,9 +4528,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390_port_set_speed, +	.port_set_speed_duplex = mv88e6390_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy, @@ -4417,7 +4541,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4439,6 +4562,11 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390_serdes_get_lane, +	/* Check status register pause & lpa register */ +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status, @@ -4448,6 +4576,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {  	.serdes_get_sset_count = mv88e6390_serdes_get_sset_count,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.phylink_validate = mv88e6390_phylink_validate,  }; @@ -4461,9 +4591,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write,  	.port_set_link = mv88e6xxx_port_set_link, -	.port_set_duplex = mv88e6xxx_port_set_duplex,  	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, -	.port_set_speed = mv88e6390x_port_set_speed, +	.port_set_speed_duplex = mv88e6390x_port_set_speed_duplex,  	.port_max_speed_mode = mv88e6390x_port_max_speed_mode,  	.port_tag_remap = mv88e6390_port_tag_remap,  	.port_set_policy = mv88e6352_port_set_policy, @@ -4475,7 +4604,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {  	.port_pause_limit = mv88e6390_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,  	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override, -	.port_link_state = mv88e6352_port_link_state,  	.port_get_cmode = mv88e6352_port_get_cmode,  	.port_set_cmode = mv88e6390x_port_set_cmode,  	.port_setup_message_port = mv88e6xxx_setup_message_port, @@ -4497,12 +4625,18 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {  	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,  	.serdes_power = mv88e6390_serdes_power,  	.serdes_get_lane = mv88e6390x_serdes_get_lane, +	.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, +	.serdes_pcs_config = mv88e6390_serdes_pcs_config, +	.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, +	.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,  	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,  	.serdes_irq_enable = mv88e6390_serdes_irq_enable,  	.serdes_irq_status = mv88e6390_serdes_irq_status,  	.serdes_get_sset_count = mv88e6390_serdes_get_sset_count,  	.serdes_get_strings = mv88e6390_serdes_get_strings,  	.serdes_get_stats = mv88e6390_serdes_get_stats, +	.serdes_get_regs_len = mv88e6390_serdes_get_regs_len, +	.serdes_get_regs = mv88e6390_serdes_get_regs,  	.gpio_ops = &mv88e6352_gpio_ops,  	.avb_ops = &mv88e6390_avb_ops,  	.ptp_ops = &mv88e6352_ptp_ops, @@ -5373,8 +5507,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {  	.setup			= mv88e6xxx_setup,  	.teardown		= mv88e6xxx_teardown,  	.phylink_validate	= mv88e6xxx_validate, -	.phylink_mac_link_state	= mv88e6xxx_link_state, +	.phylink_mac_link_state	= mv88e6xxx_serdes_pcs_get_state,  	.phylink_mac_config	= mv88e6xxx_mac_config, +	.phylink_mac_an_restart	= mv88e6xxx_serdes_pcs_an_restart,  	.phylink_mac_link_down	= mv88e6xxx_mac_link_down,  	.phylink_mac_link_up	= mv88e6xxx_mac_link_up,  	.get_strings		= mv88e6xxx_get_strings, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 79cad5e751c6..e5430cf2ad71 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -399,15 +399,6 @@ struct mv88e6xxx_ops {  	 */  	int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); -#define DUPLEX_UNFORCED		-2 - -	/* Port's MAC duplex mode -	 * -	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, -	 * or DUPLEX_UNFORCED for normal duplex detection. -	 */ -	int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); -  #define PAUSE_ON		1  #define PAUSE_OFF		0 @@ -417,13 +408,18 @@ struct mv88e6xxx_ops {  #define SPEED_MAX		INT_MAX  #define SPEED_UNFORCED		-2 +#define DUPLEX_UNFORCED		-2 -	/* Port's MAC speed (in Mbps) +	/* Port's MAC speed (in Mbps) and MAC duplex mode  	 *  	 * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.  	 * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. +	 * +	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, +	 * or DUPLEX_UNFORCED for normal duplex detection.  	 */ -	int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); +	int (*port_set_speed_duplex)(struct mv88e6xxx_chip *chip, int port, +				     int speed, int duplex);  	/* What interface mode should be used for maximum speed? */  	phy_interface_t (*port_max_speed_mode)(int port); @@ -462,9 +458,6 @@ struct mv88e6xxx_ops {  	 */  	int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port,  				      int upstream_port); -	/* Return the port link state, as required by phylink */ -	int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, -			       struct phylink_link_state *state);  	/* Snapshot the statistics for a port. The statistics can then  	 * be read back a leisure but still with a consistent view. @@ -502,6 +495,17 @@ struct mv88e6xxx_ops {  	/* SERDES lane mapping */  	u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); +	int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port, +				    u8 lane, struct phylink_link_state *state); +	int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port, +				 u8 lane, unsigned int mode, +				 phy_interface_t interface, +				 const unsigned long *advertise); +	int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port, +				     u8 lane); +	int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port, +				  u8 lane, int speed, int duplex); +  	/* SERDES interrupt handling */  	unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,  					   int port); @@ -517,6 +521,11 @@ struct mv88e6xxx_ops {  	int (*serdes_get_stats)(struct mv88e6xxx_chip *chip,  int port,  				uint64_t *data); +	/* SERDES registers for ethtool */ +	int (*serdes_get_regs_len)(struct mv88e6xxx_chip *chip,  int port); +	void (*serdes_get_regs)(struct mv88e6xxx_chip *chip, int port, +				void *_p); +  	/* Address Translation Unit operations */  	int (*atu_get_hash)(struct mv88e6xxx_chip *chip, u8 *hash);  	int (*atu_set_hash)(struct mv88e6xxx_chip *chip, u8 hash); @@ -664,9 +673,6 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg,  			u16 mask, u16 val);  int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg,  		       int bit, int val); -int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, -			     int speed, int duplex, int pause, -			     phy_interface_t mode);  struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip);  static inline void mv88e6xxx_reg_lock(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 0b43c650e100..8128dc607cf4 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -162,46 +162,9 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)  	return 0;  } -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) -{ -	u16 reg; -	int err; - -	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); -	if (err) -		return err; - -	reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | -		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); - -	switch (dup) { -	case DUPLEX_HALF: -		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; -		break; -	case DUPLEX_FULL: -		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | -			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; -		break; -	case DUPLEX_UNFORCED: -		/* normal duplex detection */ -		break; -	default: -		return -EOPNOTSUPP; -	} - -	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); -	if (err) -		return err; - -	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, -		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", -		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); - -	return 0; -} - -static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, -				    int speed, bool alt_bit, bool force_bit) +static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip, +					   int port, int speed, bool alt_bit, +					   bool force_bit, int duplex)  {  	u16 reg, ctrl;  	int err; @@ -239,11 +202,29 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,  		return -EOPNOTSUPP;  	} +	switch (duplex) { +	case DUPLEX_HALF: +		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; +		break; +	case DUPLEX_FULL: +		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | +			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; +		break; +	case DUPLEX_UNFORCED: +		/* normal duplex detection */ +		break; +	default: +		return -EOPNOTSUPP; +	} +  	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);  	if (err)  		return err; -	reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; +	reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK | +		 MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | +		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); +  	if (alt_bit)  		reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;  	if (force_bit) { @@ -261,12 +242,16 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,  		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);  	else  		dev_dbg(chip->dev, "p%d: Speed unforced\n", port); +	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, +		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", +		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");  	return 0;  }  /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = 200; @@ -275,11 +260,13 @@ int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  		return -EOPNOTSUPP;  	/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ -	return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, +					       duplex);  }  /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = 1000; @@ -287,11 +274,13 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed == 200 || speed > 1000)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, +					       duplex);  }  /* Support 10, 100 Mbps (e.g. 88E6250 family) */ -int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = 100; @@ -299,11 +288,13 @@ int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed > 100)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, +					       duplex);  }  /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = port < 5 ? 1000 : 2500; @@ -317,7 +308,8 @@ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed == 2500 && port < 5)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, !port, true); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true, +					       duplex);  }  phy_interface_t mv88e6341_port_max_speed_mode(int port) @@ -329,7 +321,8 @@ phy_interface_t mv88e6341_port_max_speed_mode(int port)  }  /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = 1000; @@ -340,11 +333,13 @@ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed == 200 && port < 5)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, true, false); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false, +					       duplex);  }  /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = port < 9 ? 1000 : 2500; @@ -358,7 +353,8 @@ int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed == 2500 && port < 9)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, +					       duplex);  }  phy_interface_t mv88e6390_port_max_speed_mode(int port) @@ -370,7 +366,8 @@ phy_interface_t mv88e6390_port_max_speed_mode(int port)  }  /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				     int speed, int duplex)  {  	if (speed == SPEED_MAX)  		speed = port < 9 ? 1000 : 10000; @@ -381,7 +378,8 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)  	if (speed >= 2500 && port < 9)  		return -EOPNOTSUPP; -	return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, +					       duplex);  }  phy_interface_t mv88e6390x_port_max_speed_mode(int port) @@ -586,183 +584,6 @@ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)  	return 0;  } -int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state) -{ -	int err; -	u16 reg; - -	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); -	if (err) -		return err; - -	if (port < 5) { -		switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { -		case MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF: -			state->speed = SPEED_10; -			state->duplex = DUPLEX_HALF; -			break; -		case MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF: -			state->speed = SPEED_100; -			state->duplex = DUPLEX_HALF; -			break; -		case MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL: -			state->speed = SPEED_10; -			state->duplex = DUPLEX_FULL; -			break; -		case MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL: -			state->speed = SPEED_100; -			state->duplex = DUPLEX_FULL; -			break; -		default: -			state->speed = SPEED_UNKNOWN; -			state->duplex = DUPLEX_UNKNOWN; -			break; -		} -	} else { -		switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { -		case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF: -			state->speed = SPEED_10; -			state->duplex = DUPLEX_HALF; -			break; -		case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF: -			state->speed = SPEED_100; -			state->duplex = DUPLEX_HALF; -			break; -		case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL: -			state->speed = SPEED_10; -			state->duplex = DUPLEX_FULL; -			break; -		case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL: -			state->speed = SPEED_100; -			state->duplex = DUPLEX_FULL; -			break; -		default: -			state->speed = SPEED_UNKNOWN; -			state->duplex = DUPLEX_UNKNOWN; -			break; -		} -	} - -	state->link = !!(reg & MV88E6250_PORT_STS_LINK); -	state->an_enabled = 1; -	state->an_complete = state->link; -	state->interface = PHY_INTERFACE_MODE_NA; - -	return 0; -} - -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state) -{ -	int err; -	u16 reg; - -	switch (chip->ports[port].cmode) { -	case MV88E6XXX_PORT_STS_CMODE_RGMII: -		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, -					  ®); -		if (err) -			return err; - -		if ((reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) && -		    (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK)) -			state->interface = PHY_INTERFACE_MODE_RGMII_ID; -		else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) -			state->interface = PHY_INTERFACE_MODE_RGMII_RXID; -		else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK) -			state->interface = PHY_INTERFACE_MODE_RGMII_TXID; -		else -			state->interface = PHY_INTERFACE_MODE_RGMII; -		break; -	case MV88E6XXX_PORT_STS_CMODE_1000BASEX: -		state->interface = PHY_INTERFACE_MODE_1000BASEX; -		break; -	case MV88E6XXX_PORT_STS_CMODE_SGMII: -		state->interface = PHY_INTERFACE_MODE_SGMII; -		break; -	case MV88E6XXX_PORT_STS_CMODE_2500BASEX: -		state->interface = PHY_INTERFACE_MODE_2500BASEX; -		break; -	case MV88E6XXX_PORT_STS_CMODE_XAUI: -		state->interface = PHY_INTERFACE_MODE_XAUI; -		break; -	case MV88E6XXX_PORT_STS_CMODE_RXAUI: -		state->interface = PHY_INTERFACE_MODE_RXAUI; -		break; -	default: -		/* we do not support other cmode values here */ -		state->interface = PHY_INTERFACE_MODE_NA; -	} - -	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); -	if (err) -		return err; - -	switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { -	case MV88E6XXX_PORT_STS_SPEED_10: -		state->speed = SPEED_10; -		break; -	case MV88E6XXX_PORT_STS_SPEED_100: -		state->speed = SPEED_100; -		break; -	case MV88E6XXX_PORT_STS_SPEED_1000: -		state->speed = SPEED_1000; -		break; -	case MV88E6XXX_PORT_STS_SPEED_10000: -		if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == -		    MV88E6XXX_PORT_STS_CMODE_2500BASEX) -			state->speed = SPEED_2500; -		else -			state->speed = SPEED_10000; -		break; -	} - -	state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? -			DUPLEX_FULL : DUPLEX_HALF; -	state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); -	state->an_enabled = 1; -	state->an_complete = state->link; - -	return 0; -} - -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state) -{ -	if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { -		u8 cmode = chip->ports[port].cmode; - -		/* When a port is in "Cross-chip serdes" mode, it uses -		 * 1000Base-X full duplex mode, but there is no automatic -		 * link detection. Use the sync OK status for link (as it -		 * would do for 1000Base-X mode.) -		 */ -		if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { -			u16 mac; -			int err; - -			err = mv88e6xxx_port_read(chip, port, -						  MV88E6XXX_PORT_MAC_CTL, &mac); -			if (err) -				return err; - -			state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); -			state->an_enabled = 1; -			state->an_complete = -				!!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); -			state->duplex = -				state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; -			state->speed = -				state->link ? SPEED_1000 : SPEED_UNKNOWN; - -			return 0; -		} -	} - -	return mv88e6352_port_link_state(chip, port, state); -} -  /* Offset 0x02: Jamming Control   *   * Do not limit the period of time that this port can be paused for by diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 0ec4327c2b42..44d76ac973f6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -298,15 +298,20 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,  int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); - -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				    int speed, int duplex); +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, +				     int speed, int duplex);  phy_interface_t mv88e6341_port_max_speed_mode(int port);  phy_interface_t mv88e6390_port_max_speed_mode(int port); @@ -359,12 +364,6 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,  			      phy_interface_t mode);  int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);  int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state); -int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state); -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, -			      struct phylink_link_state *state);  int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);  int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,  				     int upstream_port); diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 8d8b3b74aee1..2098f19b534d 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -49,6 +49,52 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,  	return mv88e6xxx_phy_write(chip, lane, reg_c45, val);  } +static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, +					  u16 status, u16 lpa, +					  struct phylink_link_state *state) +{ +	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { +		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK); +		state->duplex = status & +				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? +			                         DUPLEX_FULL : DUPLEX_HALF; + +		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE) +			state->pause |= MLO_PAUSE_TX; +		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE) +			state->pause |= MLO_PAUSE_RX; + +		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { +		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: +			if (state->interface == PHY_INTERFACE_MODE_2500BASEX) +				state->speed = SPEED_2500; +			else +				state->speed = SPEED_1000; +			break; +		case MV88E6390_SGMII_PHY_STATUS_SPEED_100: +			state->speed = SPEED_100; +			break; +		case MV88E6390_SGMII_PHY_STATUS_SPEED_10: +			state->speed = SPEED_10; +			break; +		default: +			dev_err(chip->dev, "invalid PHY speed\n"); +			return -EINVAL; +		} +	} else { +		state->link = false; +	} + +	if (state->interface == PHY_INTERFACE_MODE_2500BASEX) +		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, +				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT); +	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) +		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, +				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + +	return 0; +} +  int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,  			   bool up)  { @@ -70,6 +116,120 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,  	return err;  } +int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, +				u8 lane, unsigned int mode, +				phy_interface_t interface, +				const unsigned long *advertise) +{ +	u16 adv, bmcr, val; +	bool changed; +	int err; + +	switch (interface) { +	case PHY_INTERFACE_MODE_SGMII: +		adv = 0x0001; +		break; + +	case PHY_INTERFACE_MODE_1000BASEX: +		adv = linkmode_adv_to_mii_adv_x(advertise, +					ETHTOOL_LINK_MODE_1000baseX_Full_BIT); +		break; + +	default: +		return 0; +	} + +	err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val); +	if (err) +		return err; + +	changed = val != adv; +	if (changed) { +		err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv); +		if (err) +			return err; +	} + +	err = mv88e6352_serdes_read(chip, MII_BMCR, &val); +	if (err) +		return err; + +	if (phylink_autoneg_inband(mode)) +		bmcr = val | BMCR_ANENABLE; +	else +		bmcr = val & ~BMCR_ANENABLE; + +	if (bmcr == val) +		return changed; + +	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); +} + +int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, +				   u8 lane, struct phylink_link_state *state) +{ +	u16 lpa, status; +	int err; + +	err = mv88e6352_serdes_read(chip, 0x11, &status); +	if (err) { +		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); +		return err; +	} + +	err = mv88e6352_serdes_read(chip, MII_LPA, &lpa); +	if (err) { +		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); +		return err; +	} + +	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); +} + +int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, +				    u8 lane) +{ +	u16 bmcr; +	int err; + +	err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr); +	if (err) +		return err; + +	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART); +} + +int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, +				 u8 lane, int speed, int duplex) +{ +	u16 val, bmcr; +	int err; + +	err = mv88e6352_serdes_read(chip, MII_BMCR, &val); +	if (err) +		return err; + +	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); +	switch (speed) { +	case SPEED_1000: +		bmcr |= BMCR_SPEED1000; +		break; +	case SPEED_100: +		bmcr |= BMCR_SPEED100; +		break; +	case SPEED_10: +		break; +	} + +	if (duplex == DUPLEX_FULL) +		bmcr |= BMCR_FULLDPLX; + +	if (bmcr == val) +		return 0; + +	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); +} +  u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)  {  	u8 cmode = chip->ports[port].cmode; @@ -180,26 +340,17 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,  static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)  { -	struct dsa_switch *ds = chip->ds; -	u16 status; -	bool up; +	u16 bmsr;  	int err; -	err = mv88e6352_serdes_read(chip, MII_BMSR, &status); -	if (err) -		return; - -	/* Status must be read twice in order to give the current link -	 * status. Otherwise the change in link status since the last -	 * read of the register is returned. -	 */ -	err = mv88e6352_serdes_read(chip, MII_BMSR, &status); -	if (err) +	/* If the link has dropped, we want to know about it. */ +	err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr); +	if (err) { +		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);  		return; +	} -	up = status & BMSR_LSTATUS; - -	dsa_port_phylink_mac_change(ds, port, up); +	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));  }  irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, @@ -237,6 +388,29 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)  	return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);  } +int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) +{ +	if (!mv88e6352_port_has_serdes(chip, port)) +		return 0; + +	return 32 * sizeof(u16); +} + +void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) +{ +	u16 *p = _p; +	u16 reg; +	int i; + +	if (!mv88e6352_port_has_serdes(chip, port)) +		return; + +	for (i = 0 ; i < 32; i++) { +		mv88e6352_serdes_read(chip, i, ®); +		p[i] = reg; +	} +} +  u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)  {  	u8 cmode = chip->ports[port].cmode; @@ -387,20 +561,18 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,  	int err;  	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, -				    MV88E6390_SGMII_CONTROL, &val); +				    MV88E6390_SGMII_BMCR, &val);  	if (err)  		return err;  	if (up) -		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | -				  MV88E6390_SGMII_CONTROL_LOOPBACK | -				  MV88E6390_SGMII_CONTROL_PDOWN); +		new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN);  	else -		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; +		new_val = val | BMCR_PDOWN;  	if (val != new_val)  		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, -					     MV88E6390_SGMII_CONTROL, new_val); +					     MV88E6390_SGMII_BMCR, new_val);  	return err;  } @@ -517,71 +689,153 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,  	return err;  } -static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, -					    int port, u8 lane) +int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, +				u8 lane, unsigned int mode, +				phy_interface_t interface, +				const unsigned long *advertise)  { -	u8 cmode = chip->ports[port].cmode; -	struct dsa_switch *ds = chip->ds; -	int duplex = DUPLEX_UNKNOWN; -	int speed = SPEED_UNKNOWN; -	phy_interface_t mode; -	int link, err; -	u16 status; +	u16 val, bmcr, adv; +	bool changed; +	int err; + +	switch (interface) { +	case PHY_INTERFACE_MODE_SGMII: +		adv = 0x0001; +		break; + +	case PHY_INTERFACE_MODE_1000BASEX: +		adv = linkmode_adv_to_mii_adv_x(advertise, +					ETHTOOL_LINK_MODE_1000baseX_Full_BIT); +		break; + +	case PHY_INTERFACE_MODE_2500BASEX: +		adv = linkmode_adv_to_mii_adv_x(advertise, +					ETHTOOL_LINK_MODE_2500baseX_Full_BIT); +		break; + +	default: +		return 0; +	} + +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_ADVERTISE, &val); +	if (err) +		return err; + +	changed = val != adv; +	if (changed) { +		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, +					     MV88E6390_SGMII_ADVERTISE, adv); +		if (err) +			return err; +	} + +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_BMCR, &val); +	if (err) +		return err; + +	if (phylink_autoneg_inband(mode)) +		bmcr = val | BMCR_ANENABLE; +	else +		bmcr = val & ~BMCR_ANENABLE; + +	/* setting ANENABLE triggers a restart of negotiation */ +	if (bmcr == val) +		return changed; + +	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, +				      MV88E6390_SGMII_BMCR, bmcr); +} + +int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, +				   u8 lane, struct phylink_link_state *state) +{ +	u16 lpa, status; +	int err;  	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,  				    MV88E6390_SGMII_PHY_STATUS, &status);  	if (err) { -		dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err); -		return; +		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); +		return err;  	} -	link = status & MV88E6390_SGMII_PHY_STATUS_LINK ? -	       LINK_FORCED_UP : LINK_FORCED_DOWN; +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_LPA, &lpa); +	if (err) { +		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); +		return err; +	} -	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { -		duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? -			 DUPLEX_FULL : DUPLEX_HALF; +	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); +} -		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { -		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: -			if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) -				speed = SPEED_2500; -			else -				speed = SPEED_1000; -			break; -		case MV88E6390_SGMII_PHY_STATUS_SPEED_100: -			speed = SPEED_100; -			break; -		case MV88E6390_SGMII_PHY_STATUS_SPEED_10: -			speed = SPEED_10; -			break; -		default: -			dev_err(chip->dev, "invalid PHY speed\n"); -			return; -		} -	} +int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, +				    u8 lane) +{ +	u16 bmcr; +	int err; -	switch (cmode) { -	case MV88E6XXX_PORT_STS_CMODE_SGMII: -		mode = PHY_INTERFACE_MODE_SGMII; +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_BMCR, &bmcr); +	if (err) +		return err; + +	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, +				      MV88E6390_SGMII_BMCR, +				      bmcr | BMCR_ANRESTART); +} + +int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, +				 u8 lane, int speed, int duplex) +{ +	u16 val, bmcr; +	int err; + +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_BMCR, &val); +	if (err) +		return err; + +	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); +	switch (speed) { +	case SPEED_2500: +	case SPEED_1000: +		bmcr |= BMCR_SPEED1000;  		break; -	case MV88E6XXX_PORT_STS_CMODE_1000BASEX: -		mode = PHY_INTERFACE_MODE_1000BASEX; +	case SPEED_100: +		bmcr |= BMCR_SPEED100;  		break; -	case MV88E6XXX_PORT_STS_CMODE_2500BASEX: -		mode = PHY_INTERFACE_MODE_2500BASEX; +	case SPEED_10:  		break; -	default: -		mode = PHY_INTERFACE_MODE_NA;  	} -	err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, -				       PAUSE_OFF, mode); -	if (err) -		dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", -			err); -	else -		dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP); +	if (duplex == DUPLEX_FULL) +		bmcr |= BMCR_FULLDPLX; + +	if (bmcr == val) +		return 0; + +	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, +				      MV88E6390_SGMII_BMCR, bmcr); +} + +static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, +					    int port, u8 lane) +{ +	u16 bmsr; +	int err; + +	/* If the link has dropped, we want to know about it. */ +	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				    MV88E6390_SGMII_BMSR, &bmsr); +	if (err) { +		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); +		return; +	} + +	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));  }  static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, @@ -652,3 +906,57 @@ unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)  {  	return irq_find_mapping(chip->g2_irq.domain, port);  } + +static const u16 mv88e6390_serdes_regs[] = { +	/* SERDES common registers */ +	0xf00a, 0xf00b, 0xf00c, +	0xf010, 0xf011, 0xf012, 0xf013, +	0xf016, 0xf017, 0xf018, +	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, +	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027, +	0xf028, 0xf029, +	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, +	0xf038, 0xf039, +	/* SGMII */ +	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, +	0x2008, +	0x200f, +	0xa000, 0xa001, 0xa002, 0xa003, +	/* 10Gbase-X */ +	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, +	0x1008, +	0x100e, 0x100f, +	0x1018, 0x1019, +	0x9000, 0x9001, 0x9002, 0x9003, 0x9004, +	0x9006, +	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016, +	/* 10Gbase-R */ +	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, +	0x1028, 0x1029, 0x102a, 0x102b, +}; + +int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) +{ +	if (mv88e6xxx_serdes_get_lane(chip, port) == 0) +		return 0; + +	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16); +} + +void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) +{ +	u16 *p = _p; +	int lane; +	u16 reg; +	int i; + +	lane = mv88e6xxx_serdes_get_lane(chip, port); +	if (lane == 0) +		return; + +	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) { +		mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, +				      mv88e6390_serdes_regs[i], ®); +		p[i] = reg; +	} +} diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index d16ef4da20b0..7990cadba4c2 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -47,14 +47,10 @@  #define MV88E6390_PCS_CONTROL_1_PDOWN		BIT(11)  /* 1000BASE-X and SGMII */ -#define MV88E6390_SGMII_CONTROL		0x2000 -#define MV88E6390_SGMII_CONTROL_RESET		BIT(15) -#define MV88E6390_SGMII_CONTROL_LOOPBACK	BIT(14) -#define MV88E6390_SGMII_CONTROL_PDOWN		BIT(11) -#define MV88E6390_SGMII_STATUS		0x2001 -#define MV88E6390_SGMII_STATUS_AN_DONE		BIT(5) -#define MV88E6390_SGMII_STATUS_REMOTE_FAULT	BIT(4) -#define MV88E6390_SGMII_STATUS_LINK		BIT(2) +#define MV88E6390_SGMII_BMCR		(0x2000 + MII_BMCR) +#define MV88E6390_SGMII_BMSR		(0x2000 + MII_BMSR) +#define MV88E6390_SGMII_ADVERTISE	(0x2000 + MII_ADVERTISE) +#define MV88E6390_SGMII_LPA		(0x2000 + MII_LPA)  #define MV88E6390_SGMII_INT_ENABLE	0xa001  #define MV88E6390_SGMII_INT_SPEED_CHANGE	BIT(14)  #define MV88E6390_SGMII_INT_DUPLEX_CHANGE	BIT(13) @@ -73,6 +69,8 @@  #define MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL	BIT(13)  #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11)  #define MV88E6390_SGMII_PHY_STATUS_LINK		BIT(10) +#define MV88E6390_SGMII_PHY_STATUS_TX_PAUSE	BIT(3) +#define MV88E6390_SGMII_PHY_STATUS_RX_PAUSE	BIT(2)  /* Packet generator pad packet checker */  #define MV88E6390_PG_CONTROL		0xf010 @@ -82,6 +80,26 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);  u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);  u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);  u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, +				u8 lane, unsigned int mode, +				phy_interface_t interface, +				const unsigned long *advertise); +int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, +				u8 lane, unsigned int mode, +				phy_interface_t interface, +				const unsigned long *advertise); +int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, +				   u8 lane, struct phylink_link_state *state); +int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, +				   u8 lane, struct phylink_link_state *state); +int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, +				    u8 lane); +int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, +				    u8 lane); +int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, +				 u8 lane, int speed, int duplex); +int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, +				 u8 lane, int speed, int duplex);  unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,  					  int port);  unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, @@ -109,6 +127,11 @@ int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,  int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,  			       uint64_t *data); +int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); +void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); +int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port); +void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); +  /* Return the (first) SERDES lane address a port is using, 0 otherwise. */  static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,  					   int port) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 3257962c147e..e2c6bf0e430e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -2,6 +2,7 @@  /* Copyright 2019 NXP Semiconductors   */  #include <uapi/linux/if_bridge.h> +#include <soc/mscc/ocelot_vcap.h>  #include <soc/mscc/ocelot_qsys.h>  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot_dev.h> @@ -12,6 +13,7 @@  #include <linux/of_net.h>  #include <linux/pci.h>  #include <linux/of.h> +#include <net/pkt_sched.h>  #include <net/dsa.h>  #include "felix.h" @@ -44,11 +46,8 @@ static int felix_fdb_add(struct dsa_switch *ds, int port,  			 const unsigned char *addr, u16 vid)  {  	struct ocelot *ocelot = ds->priv; -	bool vlan_aware; -	vlan_aware = dsa_port_is_vlan_filtering(dsa_to_port(ds, port)); - -	return ocelot_fdb_add(ocelot, port, addr, vid, vlan_aware); +	return ocelot_fdb_add(ocelot, port, addr, vid);  }  static int felix_fdb_del(struct dsa_switch *ds, int port, @@ -176,8 +175,7 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port,  	phylink_set(mask, 100baseT_Full);  	phylink_set(mask, 1000baseT_Full); -	/* The internal ports that run at 2.5G are overclocked GMII */ -	if (state->interface == PHY_INTERFACE_MODE_GMII || +	if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||  	    state->interface == PHY_INTERFACE_MODE_2500BASEX ||  	    state->interface == PHY_INTERFACE_MODE_USXGMII) {  		phylink_set(mask, 2500baseT_Full); @@ -264,7 +262,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,  static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,  				      unsigned int link_an_mode,  				      phy_interface_t interface, -				      struct phy_device *phydev) +				      struct phy_device *phydev, +				      int speed, int duplex, +				      bool tx_pause, bool rx_pause)  {  	struct ocelot *ocelot = ds->priv;  	struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -400,6 +400,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  	ocelot->stats_layout	= felix->info->stats_layout;  	ocelot->num_stats	= felix->info->num_stats;  	ocelot->shared_queue_sz	= felix->info->shared_queue_sz; +	ocelot->num_mact_rows	= felix->info->num_mact_rows; +	ocelot->vcap_is2_keys	= felix->info->vcap_is2_keys; +	ocelot->vcap_is2_actions= felix->info->vcap_is2_actions; +	ocelot->vcap		= felix->info->vcap;  	ocelot->ops		= felix->info->ops;  	port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), @@ -511,12 +515,23 @@ static int felix_setup(struct dsa_switch *ds)  	for (port = 0; port < ds->num_ports; port++) {  		ocelot_init_port(ocelot, port); +		/* Bring up the CPU port module and configure the NPI port */  		if (dsa_is_cpu_port(ds, port)) -			ocelot_set_cpu_port(ocelot, port, -					    OCELOT_TAG_PREFIX_NONE, -					    OCELOT_TAG_PREFIX_LONG); +			ocelot_configure_cpu(ocelot, port, +					     OCELOT_TAG_PREFIX_NONE, +					     OCELOT_TAG_PREFIX_LONG);  	} +	/* Include the CPU port module in the forwarding mask for unknown +	 * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST +	 * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since +	 * Ocelot relies on whitelisting MAC addresses towards PGID_CPU. +	 */ +	ocelot_write_rix(ocelot, +			 ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)), +			 ANA_PGID_PGID, PGID_UC); + +	ds->mtu_enforcement_ingress = true;  	/* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040)  	 * isn't instantiated for the Felix PF.  	 * In-band AN may take a few ms to complete, so we need to poll. @@ -594,6 +609,67 @@ static bool felix_txtstamp(struct dsa_switch *ds, int port,  	return false;  } +static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ +	struct ocelot *ocelot = ds->priv; + +	ocelot_port_set_maxlen(ocelot, port, new_mtu); + +	return 0; +} + +static int felix_get_max_mtu(struct dsa_switch *ds, int port) +{ +	struct ocelot *ocelot = ds->priv; + +	return ocelot_get_max_mtu(ocelot, port); +} + +static int felix_cls_flower_add(struct dsa_switch *ds, int port, +				struct flow_cls_offload *cls, bool ingress) +{ +	struct ocelot *ocelot = ds->priv; + +	return ocelot_cls_flower_replace(ocelot, port, cls, ingress); +} + +static int felix_cls_flower_del(struct dsa_switch *ds, int port, +				struct flow_cls_offload *cls, bool ingress) +{ +	struct ocelot *ocelot = ds->priv; + +	return ocelot_cls_flower_destroy(ocelot, port, cls, ingress); +} + +static int felix_cls_flower_stats(struct dsa_switch *ds, int port, +				  struct flow_cls_offload *cls, bool ingress) +{ +	struct ocelot *ocelot = ds->priv; + +	return ocelot_cls_flower_stats(ocelot, port, cls, ingress); +} + +static int felix_port_policer_add(struct dsa_switch *ds, int port, +				  struct dsa_mall_policer_tc_entry *policer) +{ +	struct ocelot *ocelot = ds->priv; +	struct ocelot_policer pol = { +		.rate = div_u64(policer->rate_bytes_per_sec, 1000) * 8, +		.burst = div_u64(policer->rate_bytes_per_sec * +				 PSCHED_NS2TICKS(policer->burst), +				 PSCHED_TICKS_PER_SEC), +	}; + +	return ocelot_port_policer_add(ocelot, port, &pol); +} + +static void felix_port_policer_del(struct dsa_switch *ds, int port) +{ +	struct ocelot *ocelot = ds->priv; + +	ocelot_port_policer_del(ocelot, port); +} +  static const struct dsa_switch_ops felix_switch_ops = {  	.get_tag_protocol	= felix_get_tag_protocol,  	.setup			= felix_setup, @@ -625,6 +701,13 @@ static const struct dsa_switch_ops felix_switch_ops = {  	.port_hwtstamp_set	= felix_hwtstamp_set,  	.port_rxtstamp		= felix_rxtstamp,  	.port_txtstamp		= felix_txtstamp, +	.port_change_mtu	= felix_change_mtu, +	.port_max_mtu		= felix_get_max_mtu, +	.port_policer_add	= felix_port_policer_add, +	.port_policer_del	= felix_port_policer_del, +	.cls_flower_add		= felix_cls_flower_add, +	.cls_flower_del		= felix_cls_flower_del, +	.cls_flower_stats	= felix_cls_flower_stats,  };  static struct felix_info *felix_instance_tbl[] = { diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 3a7580015b62..9af106513e53 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -15,9 +15,13 @@ struct felix_info {  	const u32 *const		*map;  	const struct ocelot_ops		*ops;  	int				shared_queue_sz; +	int				num_mact_rows;  	const struct ocelot_stat_layout	*stats_layout;  	unsigned int			num_stats;  	int				num_ports; +	struct vcap_field		*vcap_is2_keys; +	struct vcap_field		*vcap_is2_actions; +	const struct vcap_props		*vcap;  	int				switch_pci_bar;  	int				imdio_pci_bar;  	int	(*mdio_bus_alloc)(struct ocelot *ocelot); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 2c812b481778..8bf395f12b47 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -3,12 +3,17 @@   * Copyright 2018-2019 NXP Semiconductors   */  #include <linux/fsl/enetc_mdio.h> +#include <soc/mscc/ocelot_vcap.h>  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot.h>  #include <linux/iopoll.h>  #include <linux/pci.h>  #include "felix.h" +#define VSC9959_VCAP_IS2_CNT		1024 +#define VSC9959_VCAP_IS2_ENTRY_WIDTH	376 +#define VSC9959_VCAP_PORT_CNT		6 +  /* TODO: should find a better place for these */  #define USXGMII_BMCR_RESET		BIT(15)  #define USXGMII_BMCR_AN_EN		BIT(12) @@ -547,6 +552,129 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = {  	{ .offset = 0x111,	.name = "drop_green_prio_7", },  }; +struct vcap_field vsc9959_vcap_is2_keys[] = { +	/* Common: 41 bits */ +	[VCAP_IS2_TYPE]				= {  0,   4}, +	[VCAP_IS2_HK_FIRST]			= {  4,   1}, +	[VCAP_IS2_HK_PAG]			= {  5,   8}, +	[VCAP_IS2_HK_IGR_PORT_MASK]		= { 13,   7}, +	[VCAP_IS2_HK_RSV2]			= { 20,   1}, +	[VCAP_IS2_HK_HOST_MATCH]		= { 21,   1}, +	[VCAP_IS2_HK_L2_MC]			= { 22,   1}, +	[VCAP_IS2_HK_L2_BC]			= { 23,   1}, +	[VCAP_IS2_HK_VLAN_TAGGED]		= { 24,   1}, +	[VCAP_IS2_HK_VID]			= { 25,  12}, +	[VCAP_IS2_HK_DEI]			= { 37,   1}, +	[VCAP_IS2_HK_PCP]			= { 38,   3}, +	/* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ +	[VCAP_IS2_HK_L2_DMAC]			= { 41,  48}, +	[VCAP_IS2_HK_L2_SMAC]			= { 89,  48}, +	/* MAC_ETYPE (TYPE=000) */ +	[VCAP_IS2_HK_MAC_ETYPE_ETYPE]		= {137,  16}, +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0]	= {153,  16}, +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1]	= {169,   8}, +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2]	= {177,   3}, +	/* MAC_LLC (TYPE=001) */ +	[VCAP_IS2_HK_MAC_LLC_L2_LLC]		= {137,  40}, +	/* MAC_SNAP (TYPE=010) */ +	[VCAP_IS2_HK_MAC_SNAP_L2_SNAP]		= {137,  40}, +	/* MAC_ARP (TYPE=011) */ +	[VCAP_IS2_HK_MAC_ARP_SMAC]		= { 41,  48}, +	[VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK]	= { 89,   1}, +	[VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK]	= { 90,   1}, +	[VCAP_IS2_HK_MAC_ARP_LEN_OK]		= { 91,   1}, +	[VCAP_IS2_HK_MAC_ARP_TARGET_MATCH]	= { 92,   1}, +	[VCAP_IS2_HK_MAC_ARP_SENDER_MATCH]	= { 93,   1}, +	[VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN]	= { 94,   1}, +	[VCAP_IS2_HK_MAC_ARP_OPCODE]		= { 95,   2}, +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP]	= { 97,  32}, +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP]	= {129,  32}, +	[VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP]	= {161,   1}, +	/* IP4_TCP_UDP / IP4_OTHER common */ +	[VCAP_IS2_HK_IP4]			= { 41,   1}, +	[VCAP_IS2_HK_L3_FRAGMENT]		= { 42,   1}, +	[VCAP_IS2_HK_L3_FRAG_OFS_GT0]		= { 43,   1}, +	[VCAP_IS2_HK_L3_OPTIONS]		= { 44,   1}, +	[VCAP_IS2_HK_IP4_L3_TTL_GT0]		= { 45,   1}, +	[VCAP_IS2_HK_L3_TOS]			= { 46,   8}, +	[VCAP_IS2_HK_L3_IP4_DIP]		= { 54,  32}, +	[VCAP_IS2_HK_L3_IP4_SIP]		= { 86,  32}, +	[VCAP_IS2_HK_DIP_EQ_SIP]		= {118,   1}, +	/* IP4_TCP_UDP (TYPE=100) */ +	[VCAP_IS2_HK_TCP]			= {119,   1}, +	[VCAP_IS2_HK_L4_SPORT]			= {120,  16}, +	[VCAP_IS2_HK_L4_DPORT]			= {136,  16}, +	[VCAP_IS2_HK_L4_RNG]			= {152,   8}, +	[VCAP_IS2_HK_L4_SPORT_EQ_DPORT]		= {160,   1}, +	[VCAP_IS2_HK_L4_SEQUENCE_EQ0]		= {161,   1}, +	[VCAP_IS2_HK_L4_URG]			= {162,   1}, +	[VCAP_IS2_HK_L4_ACK]			= {163,   1}, +	[VCAP_IS2_HK_L4_PSH]			= {164,   1}, +	[VCAP_IS2_HK_L4_RST]			= {165,   1}, +	[VCAP_IS2_HK_L4_SYN]			= {166,   1}, +	[VCAP_IS2_HK_L4_FIN]			= {167,   1}, +	[VCAP_IS2_HK_L4_1588_DOM]		= {168,   8}, +	[VCAP_IS2_HK_L4_1588_VER]		= {176,   4}, +	/* IP4_OTHER (TYPE=101) */ +	[VCAP_IS2_HK_IP4_L3_PROTO]		= {119,   8}, +	[VCAP_IS2_HK_L3_PAYLOAD]		= {127,  56}, +	/* IP6_STD (TYPE=110) */ +	[VCAP_IS2_HK_IP6_L3_TTL_GT0]		= { 41,   1}, +	[VCAP_IS2_HK_L3_IP6_SIP]		= { 42, 128}, +	[VCAP_IS2_HK_IP6_L3_PROTO]		= {170,   8}, +	/* OAM (TYPE=111) */ +	[VCAP_IS2_HK_OAM_MEL_FLAGS]		= {137,   7}, +	[VCAP_IS2_HK_OAM_VER]			= {144,   5}, +	[VCAP_IS2_HK_OAM_OPCODE]		= {149,   8}, +	[VCAP_IS2_HK_OAM_FLAGS]			= {157,   8}, +	[VCAP_IS2_HK_OAM_MEPID]			= {165,  16}, +	[VCAP_IS2_HK_OAM_CCM_CNTS_EQ0]		= {181,   1}, +	[VCAP_IS2_HK_OAM_IS_Y1731]		= {182,   1}, +}; + +struct vcap_field vsc9959_vcap_is2_actions[] = { +	[VCAP_IS2_ACT_HIT_ME_ONCE]		= {  0,  1}, +	[VCAP_IS2_ACT_CPU_COPY_ENA]		= {  1,  1}, +	[VCAP_IS2_ACT_CPU_QU_NUM]		= {  2,  3}, +	[VCAP_IS2_ACT_MASK_MODE]		= {  5,  2}, +	[VCAP_IS2_ACT_MIRROR_ENA]		= {  7,  1}, +	[VCAP_IS2_ACT_LRN_DIS]			= {  8,  1}, +	[VCAP_IS2_ACT_POLICE_ENA]		= {  9,  1}, +	[VCAP_IS2_ACT_POLICE_IDX]		= { 10,  9}, +	[VCAP_IS2_ACT_POLICE_VCAP_ONLY]		= { 19,  1}, +	[VCAP_IS2_ACT_PORT_MASK]		= { 20, 11}, +	[VCAP_IS2_ACT_REW_OP]			= { 31,  9}, +	[VCAP_IS2_ACT_SMAC_REPLACE_ENA]		= { 40,  1}, +	[VCAP_IS2_ACT_RSV]			= { 41,  2}, +	[VCAP_IS2_ACT_ACL_ID]			= { 43,  6}, +	[VCAP_IS2_ACT_HIT_CNT]			= { 49, 32}, +}; + +static const struct vcap_props vsc9959_vcap_props[] = { +	[VCAP_IS2] = { +		.tg_width = 2, +		.sw_count = 4, +		.entry_count = VSC9959_VCAP_IS2_CNT, +		.entry_width = VSC9959_VCAP_IS2_ENTRY_WIDTH, +		.action_count = VSC9959_VCAP_IS2_CNT + +				VSC9959_VCAP_PORT_CNT + 2, +		.action_width = 89, +		.action_type_width = 1, +		.action_table = { +			[IS2_ACTION_TYPE_NORMAL] = { +				.width = 44, +				.count = 2 +			}, +			[IS2_ACTION_TYPE_SMAC_SIP] = { +				.width = 6, +				.count = 4 +			}, +		}, +		.counter_words = 4, +		.counter_width = 32, +	}, +}; +  #define VSC9959_INIT_TIMEOUT			50000  #define VSC9959_GCB_RST_SLEEP			100  #define VSC9959_SYS_RAMINIT_SLEEP		80 @@ -955,8 +1083,7 @@ static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port,  					phy_interface_t phy_mode)  {  	switch (phy_mode) { -	case PHY_INTERFACE_MODE_GMII: -		/* Only supported on internal to-CPU ports */ +	case PHY_INTERFACE_MODE_INTERNAL:  		if (port != 4 && port != 5)  			return -ENOTSUPP;  		return 0; @@ -1089,7 +1216,11 @@ struct felix_info felix_info_vsc9959 = {  	.ops			= &vsc9959_ops,  	.stats_layout		= vsc9959_stats_layout,  	.num_stats		= ARRAY_SIZE(vsc9959_stats_layout), +	.vcap_is2_keys		= vsc9959_vcap_is2_keys, +	.vcap_is2_actions	= vsc9959_vcap_is2_actions, +	.vcap			= vsc9959_vcap_props,  	.shared_queue_sz	= 128 * 1024, +	.num_mact_rows		= 2048,  	.num_ports		= 6,  	.switch_pci_bar		= 4,  	.imdio_pci_bar		= 0, diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index de25f99e995a..7c86056b9401 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -458,7 +458,9 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,  static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,  					  unsigned int mode,  					  phy_interface_t interface, -					  struct phy_device *phydev) +					  struct phy_device *phydev, +					  int speed, int duplex, +					  bool tx_pause, bool rx_pause)  {  	struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;  	struct regmap *regmap = priv->regmap; diff --git a/drivers/net/dsa/sja1105/Kconfig b/drivers/net/dsa/sja1105/Kconfig index 0fe1ae173aa1..68c3086af9af 100644 --- a/drivers/net/dsa/sja1105/Kconfig +++ b/drivers/net/dsa/sja1105/Kconfig @@ -20,6 +20,7 @@ tristate "NXP SJA1105 Ethernet switch family support"  config NET_DSA_SJA1105_PTP  	bool "Support for the PTP clock on the NXP SJA1105 Ethernet switch"  	depends on NET_DSA_SJA1105 +	depends on PTP_1588_CLOCK  	help  	  This enables support for timestamping and PTP clock manipulations in  	  the SJA1105 DSA driver. diff --git a/drivers/net/dsa/sja1105/Makefile b/drivers/net/dsa/sja1105/Makefile index 66161e874344..8943d8d66f2b 100644 --- a/drivers/net/dsa/sja1105/Makefile +++ b/drivers/net/dsa/sja1105/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_NET_DSA_SJA1105) += sja1105.o  sja1105-objs := \      sja1105_spi.o \      sja1105_main.o \ +    sja1105_flower.o \      sja1105_ethtool.o \      sja1105_clocking.o \      sja1105_static_config.o \ diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index d801fc204d19..8b60dbd567f2 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -19,6 +19,7 @@   * The passed parameter is in multiples of 1 ms.   */  #define SJA1105_AGEING_TIME_MS(ms)	((ms) / 10) +#define SJA1105_NUM_L2_POLICERS		45  typedef enum {  	SPI_READ = 0, @@ -36,11 +37,15 @@ struct sja1105_regs {  	u64 port_control;  	u64 rgu;  	u64 config; +	u64 sgmii;  	u64 rmii_pll1; +	u64 ptppinst; +	u64 ptppindur;  	u64 ptp_control;  	u64 ptpclkval;  	u64 ptpclkrate;  	u64 ptpclkcorp; +	u64 ptpsyncts;  	u64 ptpschtm;  	u64 ptpegr_ts[SJA1105_NUM_PORTS];  	u64 pad_mii_tx[SJA1105_NUM_PORTS]; @@ -56,6 +61,7 @@ struct sja1105_regs {  	u64 mac[SJA1105_NUM_PORTS];  	u64 mac_hl1[SJA1105_NUM_PORTS];  	u64 mac_hl2[SJA1105_NUM_PORTS]; +	u64 ether_stats[SJA1105_NUM_PORTS];  	u64 qlevel[SJA1105_NUM_PORTS];  }; @@ -90,6 +96,36 @@ struct sja1105_info {  	const char *name;  }; +enum sja1105_rule_type { +	SJA1105_RULE_BCAST_POLICER, +	SJA1105_RULE_TC_POLICER, +}; + +struct sja1105_rule { +	struct list_head list; +	unsigned long cookie; +	unsigned long port_mask; +	enum sja1105_rule_type type; + +	union { +		/* SJA1105_RULE_BCAST_POLICER */ +		struct { +			int sharindx; +		} bcast_pol; + +		/* SJA1105_RULE_TC_POLICER */ +		struct { +			int sharindx; +			int tc; +		} tc_pol; +	}; +}; + +struct sja1105_flow_block { +	struct list_head rules; +	bool l2_policer_used[SJA1105_NUM_L2_POLICERS]; +}; +  struct sja1105_private {  	struct sja1105_static_config static_config;  	bool rgmii_rx_delay[SJA1105_NUM_PORTS]; @@ -98,6 +134,7 @@ struct sja1105_private {  	struct gpio_desc *reset_gpio;  	struct spi_device *spidev;  	struct dsa_switch *ds; +	struct sja1105_flow_block flow_block;  	struct sja1105_port ports[SJA1105_NUM_PORTS];  	/* Serializes transmission of management frames so that  	 * the switch doesn't confuse them with one another. @@ -122,6 +159,7 @@ enum sja1105_reset_reason {  	SJA1105_RX_HWTSTAMPING,  	SJA1105_AGEING_TIME,  	SJA1105_SCHEDULING, +	SJA1105_BEST_EFFORT_POLICING,  };  int sja1105_static_config_reload(struct sja1105_private *priv, @@ -159,6 +197,7 @@ typedef enum {  	XMII_MODE_MII		= 0,  	XMII_MODE_RMII		= 1,  	XMII_MODE_RGMII		= 2, +	XMII_MODE_SGMII		= 3,  } sja1105_phy_interface_t;  typedef enum { @@ -212,5 +251,15 @@ size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,  					 enum packing_op op);  size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,  					    enum packing_op op); +size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr, +					    enum packing_op op); + +/* From sja1105_flower.c */ +int sja1105_cls_flower_del(struct dsa_switch *ds, int port, +			   struct flow_cls_offload *cls, bool ingress); +int sja1105_cls_flower_add(struct dsa_switch *ds, int port, +			   struct flow_cls_offload *cls, bool ingress); +void sja1105_flower_setup(struct dsa_switch *ds); +void sja1105_flower_teardown(struct dsa_switch *ds);  #endif diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c index 9082e52b55e9..0fdc2d55fff6 100644 --- a/drivers/net/dsa/sja1105/sja1105_clocking.c +++ b/drivers/net/dsa/sja1105/sja1105_clocking.c @@ -660,6 +660,10 @@ int sja1105_clocking_setup_port(struct sja1105_private *priv, int port)  	case XMII_MODE_RGMII:  		rc = sja1105_rgmii_clocking_setup(priv, port, role);  		break; +	case XMII_MODE_SGMII: +		/* Nothing to do in the CGU for SGMII */ +		rc = 0; +		break;  	default:  		dev_err(dev, "Invalid interface mode specified: %d\n",  			phy_mode); diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 25381bd65ed7..bf9b36ff35bf 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -124,6 +124,9 @@  #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD			\  	SJA1105_SIZE_DYN_CMD +#define SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD			\ +	(SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY) +  #define SJA1105_MAX_DYN_CMD_SIZE				\  	SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD @@ -481,6 +484,18 @@ sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,  	return 0;  } +static void +sja1105pqrs_avb_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, +				   enum packing_op op) +{ +	u8 *p = buf + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY; +	const int size = SJA1105_SIZE_DYN_CMD; + +	sja1105_packing(p, &cmd->valid,   31, 31, size, op); +	sja1105_packing(p, &cmd->errors,  30, 30, size, op); +	sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); +} +  #define OP_READ		BIT(0)  #define OP_WRITE	BIT(1)  #define OP_DEL		BIT(2) @@ -610,7 +625,14 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {  		.addr = 0x38,  	},  	[BLK_IDX_L2_FORWARDING_PARAMS] = {0}, -	[BLK_IDX_AVB_PARAMS] = {0}, +	[BLK_IDX_AVB_PARAMS] = { +		.entry_packing = sja1105pqrs_avb_params_entry_packing, +		.cmd_packing = sja1105pqrs_avb_params_cmd_packing, +		.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT, +		.access = (OP_READ | OP_WRITE), +		.packed_size = SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD, +		.addr = 0x8003, +	},  	[BLK_IDX_GENERAL_PARAMS] = {  		.entry_packing = sja1105et_general_params_entry_packing,  		.cmd_packing = sja1105et_general_params_cmd_packing, diff --git a/drivers/net/dsa/sja1105/sja1105_ethtool.c b/drivers/net/dsa/sja1105/sja1105_ethtool.c index 064301cc7d5b..d742ffcbfce9 100644 --- a/drivers/net/dsa/sja1105/sja1105_ethtool.c +++ b/drivers/net/dsa/sja1105/sja1105_ethtool.c @@ -7,6 +7,7 @@  #define SJA1105_SIZE_HL1_AREA		(0x10 * 4)  #define SJA1105_SIZE_HL2_AREA		(0x4 * 4)  #define SJA1105_SIZE_QLEVEL_AREA	(0x8 * 4) /* 0x4 to 0xB */ +#define SJA1105_SIZE_ETHER_AREA		(0x17 * 4)  struct sja1105_port_status_mac {  	u64 n_runt; @@ -63,10 +64,37 @@ struct sja1105_port_status_hl2 {  	u64 qlevel[8];     /* Only for P/Q/R/S */  }; +struct sja1105_port_status_ether { +	u64 n_drops_nolearn; +	u64 n_drops_noroute; +	u64 n_drops_ill_dtag; +	u64 n_drops_dtag; +	u64 n_drops_sotag; +	u64 n_drops_sitag; +	u64 n_drops_utag; +	u64 n_tx_bytes_1024_2047; +	u64 n_tx_bytes_512_1023; +	u64 n_tx_bytes_256_511; +	u64 n_tx_bytes_128_255; +	u64 n_tx_bytes_65_127; +	u64 n_tx_bytes_64; +	u64 n_tx_mcast; +	u64 n_tx_bcast; +	u64 n_rx_bytes_1024_2047; +	u64 n_rx_bytes_512_1023; +	u64 n_rx_bytes_256_511; +	u64 n_rx_bytes_128_255; +	u64 n_rx_bytes_65_127; +	u64 n_rx_bytes_64; +	u64 n_rx_mcast; +	u64 n_rx_bcast; +}; +  struct sja1105_port_status {  	struct sja1105_port_status_mac mac;  	struct sja1105_port_status_hl1 hl1;  	struct sja1105_port_status_hl2 hl2; +	struct sja1105_port_status_ether ether;  };  static void @@ -158,6 +186,58 @@ sja1105pqrs_port_status_qlevel_unpack(void *buf,  	}  } +static void +sja1105pqrs_port_status_ether_unpack(void *buf, +				     struct sja1105_port_status_ether *status) +{ +	/* Make pointer arithmetic work on 4 bytes */ +	u32 *p = buf; + +	sja1105_unpack(p + 0x16, &status->n_drops_nolearn,      31, 0, 4); +	sja1105_unpack(p + 0x15, &status->n_drops_noroute,      31, 0, 4); +	sja1105_unpack(p + 0x14, &status->n_drops_ill_dtag,     31, 0, 4); +	sja1105_unpack(p + 0x13, &status->n_drops_dtag,         31, 0, 4); +	sja1105_unpack(p + 0x12, &status->n_drops_sotag,        31, 0, 4); +	sja1105_unpack(p + 0x11, &status->n_drops_sitag,        31, 0, 4); +	sja1105_unpack(p + 0x10, &status->n_drops_utag,         31, 0, 4); +	sja1105_unpack(p + 0x0F, &status->n_tx_bytes_1024_2047, 31, 0, 4); +	sja1105_unpack(p + 0x0E, &status->n_tx_bytes_512_1023,  31, 0, 4); +	sja1105_unpack(p + 0x0D, &status->n_tx_bytes_256_511,   31, 0, 4); +	sja1105_unpack(p + 0x0C, &status->n_tx_bytes_128_255,   31, 0, 4); +	sja1105_unpack(p + 0x0B, &status->n_tx_bytes_65_127,    31, 0, 4); +	sja1105_unpack(p + 0x0A, &status->n_tx_bytes_64,        31, 0, 4); +	sja1105_unpack(p + 0x09, &status->n_tx_mcast,           31, 0, 4); +	sja1105_unpack(p + 0x08, &status->n_tx_bcast,           31, 0, 4); +	sja1105_unpack(p + 0x07, &status->n_rx_bytes_1024_2047, 31, 0, 4); +	sja1105_unpack(p + 0x06, &status->n_rx_bytes_512_1023,  31, 0, 4); +	sja1105_unpack(p + 0x05, &status->n_rx_bytes_256_511,   31, 0, 4); +	sja1105_unpack(p + 0x04, &status->n_rx_bytes_128_255,   31, 0, 4); +	sja1105_unpack(p + 0x03, &status->n_rx_bytes_65_127,    31, 0, 4); +	sja1105_unpack(p + 0x02, &status->n_rx_bytes_64,        31, 0, 4); +	sja1105_unpack(p + 0x01, &status->n_rx_mcast,           31, 0, 4); +	sja1105_unpack(p + 0x00, &status->n_rx_bcast,           31, 0, 4); +} + +static int +sja1105pqrs_port_status_get_ether(struct sja1105_private *priv, +				  struct sja1105_port_status_ether *ether, +				  int port) +{ +	const struct sja1105_regs *regs = priv->info->regs; +	u8 packed_buf[SJA1105_SIZE_ETHER_AREA] = {0}; +	int rc; + +	/* Ethernet statistics area */ +	rc = sja1105_xfer_buf(priv, SPI_READ, regs->ether_stats[port], +			      packed_buf, SJA1105_SIZE_ETHER_AREA); +	if (rc < 0) +		return rc; + +	sja1105pqrs_port_status_ether_unpack(packed_buf, ether); + +	return 0; +} +  static int sja1105_port_status_get_mac(struct sja1105_private *priv,  				       struct sja1105_port_status_mac *status,  				       int port) @@ -241,7 +321,11 @@ static int sja1105_port_status_get(struct sja1105_private *priv,  	if (rc < 0)  		return rc; -	return 0; +	if (priv->info->device_id == SJA1105E_DEVICE_ID || +	    priv->info->device_id == SJA1105T_DEVICE_ID) +		return 0; + +	return sja1105pqrs_port_status_get_ether(priv, &status->ether, port);  }  static char sja1105_port_stats[][ETH_GSTRING_LEN] = { @@ -308,6 +392,30 @@ static char sja1105pqrs_extra_port_stats[][ETH_GSTRING_LEN] = {  	"qlevel_5",  	"qlevel_6",  	"qlevel_7", +	/* Ether Stats */ +	"n_drops_nolearn", +	"n_drops_noroute", +	"n_drops_ill_dtag", +	"n_drops_dtag", +	"n_drops_sotag", +	"n_drops_sitag", +	"n_drops_utag", +	"n_tx_bytes_1024_2047", +	"n_tx_bytes_512_1023", +	"n_tx_bytes_256_511", +	"n_tx_bytes_128_255", +	"n_tx_bytes_65_127", +	"n_tx_bytes_64", +	"n_tx_mcast", +	"n_tx_bcast", +	"n_rx_bytes_1024_2047", +	"n_rx_bytes_512_1023", +	"n_rx_bytes_256_511", +	"n_rx_bytes_128_255", +	"n_rx_bytes_65_127", +	"n_rx_bytes_64", +	"n_rx_mcast", +	"n_rx_bcast",  };  void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) @@ -376,6 +484,29 @@ void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)  		data[k++] = status.hl2.qlevel_hwm[i];  		data[k++] = status.hl2.qlevel[i];  	} +	data[k++] = status.ether.n_drops_nolearn; +	data[k++] = status.ether.n_drops_noroute; +	data[k++] = status.ether.n_drops_ill_dtag; +	data[k++] = status.ether.n_drops_dtag; +	data[k++] = status.ether.n_drops_sotag; +	data[k++] = status.ether.n_drops_sitag; +	data[k++] = status.ether.n_drops_utag; +	data[k++] = status.ether.n_tx_bytes_1024_2047; +	data[k++] = status.ether.n_tx_bytes_512_1023; +	data[k++] = status.ether.n_tx_bytes_256_511; +	data[k++] = status.ether.n_tx_bytes_128_255; +	data[k++] = status.ether.n_tx_bytes_65_127; +	data[k++] = status.ether.n_tx_bytes_64; +	data[k++] = status.ether.n_tx_mcast; +	data[k++] = status.ether.n_tx_bcast; +	data[k++] = status.ether.n_rx_bytes_1024_2047; +	data[k++] = status.ether.n_rx_bytes_512_1023; +	data[k++] = status.ether.n_rx_bytes_256_511; +	data[k++] = status.ether.n_rx_bytes_128_255; +	data[k++] = status.ether.n_rx_bytes_65_127; +	data[k++] = status.ether.n_rx_bytes_64; +	data[k++] = status.ether.n_rx_mcast; +	data[k++] = status.ether.n_rx_bcast;  }  void sja1105_get_strings(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c new file mode 100644 index 000000000000..5288a722e625 --- /dev/null +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2020, NXP Semiconductors + */ +#include "sja1105.h" + +static struct sja1105_rule *sja1105_rule_find(struct sja1105_private *priv, +					      unsigned long cookie) +{ +	struct sja1105_rule *rule; + +	list_for_each_entry(rule, &priv->flow_block.rules, list) +		if (rule->cookie == cookie) +			return rule; + +	return NULL; +} + +static int sja1105_find_free_l2_policer(struct sja1105_private *priv) +{ +	int i; + +	for (i = 0; i < SJA1105_NUM_L2_POLICERS; i++) +		if (!priv->flow_block.l2_policer_used[i]) +			return i; + +	return -1; +} + +static int sja1105_setup_bcast_policer(struct sja1105_private *priv, +				       struct netlink_ext_ack *extack, +				       unsigned long cookie, int port, +				       u64 rate_bytes_per_sec, +				       s64 burst) +{ +	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); +	struct sja1105_l2_policing_entry *policing; +	bool new_rule = false; +	unsigned long p; +	int rc; + +	if (!rule) { +		rule = kzalloc(sizeof(*rule), GFP_KERNEL); +		if (!rule) +			return -ENOMEM; + +		rule->cookie = cookie; +		rule->type = SJA1105_RULE_BCAST_POLICER; +		rule->bcast_pol.sharindx = sja1105_find_free_l2_policer(priv); +		new_rule = true; +	} + +	if (rule->bcast_pol.sharindx == -1) { +		NL_SET_ERR_MSG_MOD(extack, "No more L2 policers free"); +		rc = -ENOSPC; +		goto out; +	} + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	if (policing[(SJA1105_NUM_PORTS * SJA1105_NUM_TC) + port].sharindx != port) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Port already has a broadcast policer"); +		rc = -EEXIST; +		goto out; +	} + +	rule->port_mask |= BIT(port); + +	/* Make the broadcast policers of all ports attached to this block +	 * point to the newly allocated policer +	 */ +	for_each_set_bit(p, &rule->port_mask, SJA1105_NUM_PORTS) { +		int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + p; + +		policing[bcast].sharindx = rule->bcast_pol.sharindx; +	} + +	policing[rule->bcast_pol.sharindx].rate = div_u64(rate_bytes_per_sec * +							  512, 1000000); +	policing[rule->bcast_pol.sharindx].smax = div_u64(rate_bytes_per_sec * +							  PSCHED_NS2TICKS(burst), +							  PSCHED_TICKS_PER_SEC); +	/* TODO: support per-flow MTU */ +	policing[rule->bcast_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN + +						    ETH_FCS_LEN; + +	rc = sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); + +out: +	if (rc == 0 && new_rule) { +		priv->flow_block.l2_policer_used[rule->bcast_pol.sharindx] = true; +		list_add(&rule->list, &priv->flow_block.rules); +	} else if (new_rule) { +		kfree(rule); +	} + +	return rc; +} + +static int sja1105_setup_tc_policer(struct sja1105_private *priv, +				    struct netlink_ext_ack *extack, +				    unsigned long cookie, int port, int tc, +				    u64 rate_bytes_per_sec, +				    s64 burst) +{ +	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); +	struct sja1105_l2_policing_entry *policing; +	bool new_rule = false; +	unsigned long p; +	int rc; + +	if (!rule) { +		rule = kzalloc(sizeof(*rule), GFP_KERNEL); +		if (!rule) +			return -ENOMEM; + +		rule->cookie = cookie; +		rule->type = SJA1105_RULE_TC_POLICER; +		rule->tc_pol.sharindx = sja1105_find_free_l2_policer(priv); +		rule->tc_pol.tc = tc; +		new_rule = true; +	} + +	if (rule->tc_pol.sharindx == -1) { +		NL_SET_ERR_MSG_MOD(extack, "No more L2 policers free"); +		rc = -ENOSPC; +		goto out; +	} + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	if (policing[(port * SJA1105_NUM_TC) + tc].sharindx != port) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Port-TC pair already has an L2 policer"); +		rc = -EEXIST; +		goto out; +	} + +	rule->port_mask |= BIT(port); + +	/* Make the policers for traffic class @tc of all ports attached to +	 * this block point to the newly allocated policer +	 */ +	for_each_set_bit(p, &rule->port_mask, SJA1105_NUM_PORTS) { +		int index = (p * SJA1105_NUM_TC) + tc; + +		policing[index].sharindx = rule->tc_pol.sharindx; +	} + +	policing[rule->tc_pol.sharindx].rate = div_u64(rate_bytes_per_sec * +						       512, 1000000); +	policing[rule->tc_pol.sharindx].smax = div_u64(rate_bytes_per_sec * +						       PSCHED_NS2TICKS(burst), +						       PSCHED_TICKS_PER_SEC); +	/* TODO: support per-flow MTU */ +	policing[rule->tc_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN + +						 ETH_FCS_LEN; + +	rc = sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); + +out: +	if (rc == 0 && new_rule) { +		priv->flow_block.l2_policer_used[rule->tc_pol.sharindx] = true; +		list_add(&rule->list, &priv->flow_block.rules); +	} else if (new_rule) { +		kfree(rule); +	} + +	return rc; +} + +static int sja1105_flower_parse_policer(struct sja1105_private *priv, int port, +					struct netlink_ext_ack *extack, +					struct flow_cls_offload *cls, +					u64 rate_bytes_per_sec, +					s64 burst) +{ +	struct flow_rule *rule = flow_cls_offload_flow_rule(cls); +	struct flow_dissector *dissector = rule->match.dissector; + +	if (dissector->used_keys & +	    ~(BIT(FLOW_DISSECTOR_KEY_BASIC) | +	      BIT(FLOW_DISSECTOR_KEY_CONTROL) | +	      BIT(FLOW_DISSECTOR_KEY_VLAN) | +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Unsupported keys used"); +		return -EOPNOTSUPP; +	} + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { +		struct flow_match_basic match; + +		flow_rule_match_basic(rule, &match); +		if (match.key->n_proto) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Matching on protocol not supported"); +			return -EOPNOTSUPP; +		} +	} + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { +		u8 bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +		u8 null[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +		struct flow_match_eth_addrs match; + +		flow_rule_match_eth_addrs(rule, &match); + +		if (!ether_addr_equal_masked(match.key->src, null, +					     match.mask->src)) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Matching on source MAC not supported"); +			return -EOPNOTSUPP; +		} + +		if (!ether_addr_equal_masked(match.key->dst, bcast, +					     match.mask->dst)) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Only matching on broadcast DMAC is supported"); +			return -EOPNOTSUPP; +		} + +		return sja1105_setup_bcast_policer(priv, extack, cls->cookie, +						   port, rate_bytes_per_sec, +						   burst); +	} + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { +		struct flow_match_vlan match; + +		flow_rule_match_vlan(rule, &match); + +		if (match.key->vlan_id & match.mask->vlan_id) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Matching on VID is not supported"); +			return -EOPNOTSUPP; +		} + +		if (match.mask->vlan_priority != 0x7) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Masked matching on PCP is not supported"); +			return -EOPNOTSUPP; +		} + +		return sja1105_setup_tc_policer(priv, extack, cls->cookie, port, +						match.key->vlan_priority, +						rate_bytes_per_sec, +						burst); +	} + +	NL_SET_ERR_MSG_MOD(extack, "Not matching on any known key"); +	return -EOPNOTSUPP; +} + +int sja1105_cls_flower_add(struct dsa_switch *ds, int port, +			   struct flow_cls_offload *cls, bool ingress) +{ +	struct flow_rule *rule = flow_cls_offload_flow_rule(cls); +	struct netlink_ext_ack *extack = cls->common.extack; +	struct sja1105_private *priv = ds->priv; +	const struct flow_action_entry *act; +	int rc = -EOPNOTSUPP, i; + +	flow_action_for_each(i, act, &rule->action) { +		switch (act->id) { +		case FLOW_ACTION_POLICE: +			rc = sja1105_flower_parse_policer(priv, port, extack, cls, +							  act->police.rate_bytes_ps, +							  act->police.burst); +			break; +		default: +			NL_SET_ERR_MSG_MOD(extack, +					   "Action not supported"); +			break; +		} +	} + +	return rc; +} + +int sja1105_cls_flower_del(struct dsa_switch *ds, int port, +			   struct flow_cls_offload *cls, bool ingress) +{ +	struct sja1105_private *priv = ds->priv; +	struct sja1105_rule *rule = sja1105_rule_find(priv, cls->cookie); +	struct sja1105_l2_policing_entry *policing; +	int old_sharindx; + +	if (!rule) +		return 0; + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	if (rule->type == SJA1105_RULE_BCAST_POLICER) { +		int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + port; + +		old_sharindx = policing[bcast].sharindx; +		policing[bcast].sharindx = port; +	} else if (rule->type == SJA1105_RULE_TC_POLICER) { +		int index = (port * SJA1105_NUM_TC) + rule->tc_pol.tc; + +		old_sharindx = policing[index].sharindx; +		policing[index].sharindx = port; +	} else { +		return -EINVAL; +	} + +	rule->port_mask &= ~BIT(port); +	if (!rule->port_mask) { +		priv->flow_block.l2_policer_used[old_sharindx] = false; +		list_del(&rule->list); +		kfree(rule); +	} + +	return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +void sja1105_flower_setup(struct dsa_switch *ds) +{ +	struct sja1105_private *priv = ds->priv; +	int port; + +	INIT_LIST_HEAD(&priv->flow_block.rules); + +	for (port = 0; port < SJA1105_NUM_PORTS; port++) +		priv->flow_block.l2_policer_used[port] = true; +} + +void sja1105_flower_teardown(struct dsa_switch *ds) +{ +	struct sja1105_private *priv = ds->priv; +	struct sja1105_rule *rule; +	struct list_head *pos, *n; + +	list_for_each_safe(pos, n, &priv->flow_block.rules) { +		rule = list_entry(pos, struct sja1105_rule, list); +		list_del(&rule->list); +		kfree(rule); +	} +} diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 7edea5741a5f..472f4eb20c49 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -22,6 +22,7 @@  #include <linux/if_ether.h>  #include <linux/dsa/8021q.h>  #include "sja1105.h" +#include "sja1105_sgmii.h"  #include "sja1105_tas.h"  static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, @@ -135,6 +136,21 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)  	return 0;  } +static bool sja1105_supports_sgmii(struct sja1105_private *priv, int port) +{ +	if (priv->info->part_no != SJA1105R_PART_NO && +	    priv->info->part_no != SJA1105S_PART_NO) +		return false; + +	if (port != SJA1105_SGMII_PORT) +		return false; + +	if (dsa_is_unused_port(priv->ds, port)) +		return false; + +	return true; +} +  static int sja1105_init_mii_settings(struct sja1105_private *priv,  				     struct sja1105_dt_port *ports)  { @@ -162,6 +178,9 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv,  	mii = table->entries;  	for (i = 0; i < SJA1105_NUM_PORTS; i++) { +		if (dsa_is_unused_port(priv->ds, i)) +			continue; +  		switch (ports[i].phy_mode) {  		case PHY_INTERFACE_MODE_MII:  			mii->xmii_mode[i] = XMII_MODE_MII; @@ -175,12 +194,24 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv,  		case PHY_INTERFACE_MODE_RGMII_TXID:  			mii->xmii_mode[i] = XMII_MODE_RGMII;  			break; +		case PHY_INTERFACE_MODE_SGMII: +			if (!sja1105_supports_sgmii(priv, i)) +				return -EINVAL; +			mii->xmii_mode[i] = XMII_MODE_SGMII; +			break;  		default:  			dev_err(dev, "Unsupported PHY mode %s!\n",  				phy_modes(ports[i].phy_mode));  		} -		mii->phy_mac[i] = ports[i].role; +		/* Even though the SerDes port is able to drive SGMII autoneg +		 * like a PHY would, from the perspective of the XMII tables, +		 * the SGMII port should always be put in MAC mode. +		 */ +		if (ports[i].phy_mode == PHY_INTERFACE_MODE_SGMII) +			mii->phy_mac[i] = XMII_MAC; +		else +			mii->phy_mac[i] = ports[i].role;  	}  	return 0;  } @@ -448,23 +479,93 @@ static int sja1105_init_general_params(struct sja1105_private *priv)  	return 0;  } -#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) - -static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing, -				  int index) +static int sja1105_init_avb_params(struct sja1105_private *priv)  { -	policing[index].sharindx = index; -	policing[index].smax = 65535; /* Burst size in bytes */ -	policing[index].rate = SJA1105_RATE_MBPS(1000); -	policing[index].maxlen = ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN; -	policing[index].partition = 0; +	struct sja1105_avb_params_entry *avb; +	struct sja1105_table *table; + +	table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS]; + +	/* Discard previous AVB Parameters Table */ +	if (table->entry_count) { +		kfree(table->entries); +		table->entry_count = 0; +	} + +	table->entries = kcalloc(SJA1105_MAX_AVB_PARAMS_COUNT, +				 table->ops->unpacked_entry_size, GFP_KERNEL); +	if (!table->entries) +		return -ENOMEM; + +	table->entry_count = SJA1105_MAX_AVB_PARAMS_COUNT; + +	avb = table->entries; + +	/* Configure the MAC addresses for meta frames */ +	avb->destmeta = SJA1105_META_DMAC; +	avb->srcmeta  = SJA1105_META_SMAC; +	/* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by +	 * default. This is because there might be boards with a hardware +	 * layout where enabling the pin as output might cause an electrical +	 * clash. On E/T the pin is always an output, which the board designers +	 * probably already knew, so even if there are going to be electrical +	 * issues, there's nothing we can do. +	 */ +	avb->cas_master = false; + +	return 0;  } +/* The L2 policing table is 2-stage. The table is looked up for each frame + * according to the ingress port, whether it was broadcast or not, and the + * classified traffic class (given by VLAN PCP). This portion of the lookup is + * fixed, and gives access to the SHARINDX, an indirection register pointing + * within the policing table itself, which is used to resolve the policer that + * will be used for this frame. + * + *  Stage 1                              Stage 2 + * +------------+--------+              +---------------------------------+ + * |Port 0 TC 0 |SHARINDX|              | Policer 0: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + * |Port 0 TC 1 |SHARINDX|              | Policer 1: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + *    ...                               | Policer 2: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + * |Port 0 TC 7 |SHARINDX|              | Policer 3: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + * |Port 1 TC 0 |SHARINDX|              | Policer 4: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + *    ...                               | Policer 5: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + * |Port 1 TC 7 |SHARINDX|              | Policer 6: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + *    ...                               | Policer 7: Rate, Burst, MTU     | + * +------------+--------+              +---------------------------------+ + * |Port 4 TC 7 |SHARINDX|                 ... + * +------------+--------+ + * |Port 0 BCAST|SHARINDX|                 ... + * +------------+--------+ + * |Port 1 BCAST|SHARINDX|                 ... + * +------------+--------+ + *    ...                                  ... + * +------------+--------+              +---------------------------------+ + * |Port 4 BCAST|SHARINDX|              | Policer 44: Rate, Burst, MTU    | + * +------------+--------+              +---------------------------------+ + * + * In this driver, we shall use policers 0-4 as statically alocated port + * (matchall) policers. So we need to make the SHARINDX for all lookups + * corresponding to this ingress port (8 VLAN PCP lookups and 1 broadcast + * lookup) equal. + * The remaining policers (40) shall be dynamically allocated for flower + * policers, where the key is either vlan_prio or dst_mac ff:ff:ff:ff:ff:ff. + */ +#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) +  static int sja1105_init_l2_policing(struct sja1105_private *priv)  {  	struct sja1105_l2_policing_entry *policing;  	struct sja1105_table *table; -	int i, j, k; +	int port, tc;  	table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; @@ -483,18 +584,29 @@ static int sja1105_init_l2_policing(struct sja1105_private *priv)  	policing = table->entries; -	/* k sweeps through all unicast policers (0-39). -	 * bcast sweeps through policers 40-44. -	 */ -	for (i = 0, k = 0; i < SJA1105_NUM_PORTS; i++) { -		int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + i; +	/* Setup shared indices for the matchall policers */ +	for (port = 0; port < SJA1105_NUM_PORTS; port++) { +		int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + port; -		for (j = 0; j < SJA1105_NUM_TC; j++, k++) -			sja1105_setup_policer(policing, k); +		for (tc = 0; tc < SJA1105_NUM_TC; tc++) +			policing[port * SJA1105_NUM_TC + tc].sharindx = port; -		/* Set up this port's policer for broadcast traffic */ -		sja1105_setup_policer(policing, bcast); +		policing[bcast].sharindx = port;  	} + +	/* Setup the matchall policer parameters */ +	for (port = 0; port < SJA1105_NUM_PORTS; port++) { +		int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; + +		if (dsa_is_cpu_port(priv->ds, port)) +			mtu += VLAN_HLEN; + +		policing[port].smax = 65535; /* Burst size in bytes */ +		policing[port].rate = SJA1105_RATE_MBPS(1000); +		policing[port].maxlen = mtu; +		policing[port].partition = 0; +	} +  	return 0;  } @@ -538,6 +650,9 @@ static int sja1105_static_config_load(struct sja1105_private *priv,  	rc = sja1105_init_general_params(priv);  	if (rc < 0)  		return rc; +	rc = sja1105_init_avb_params(priv); +	if (rc < 0) +		return rc;  	/* Send initial configuration to hardware via SPI */  	return sja1105_static_config_upload(priv); @@ -647,6 +762,85 @@ static int sja1105_parse_dt(struct sja1105_private *priv,  	return rc;  } +static int sja1105_sgmii_read(struct sja1105_private *priv, int pcs_reg) +{ +	const struct sja1105_regs *regs = priv->info->regs; +	u32 val; +	int rc; + +	rc = sja1105_xfer_u32(priv, SPI_READ, regs->sgmii + pcs_reg, &val, +			      NULL); +	if (rc < 0) +		return rc; + +	return val; +} + +static int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg, +			       u16 pcs_val) +{ +	const struct sja1105_regs *regs = priv->info->regs; +	u32 val = pcs_val; +	int rc; + +	rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->sgmii + pcs_reg, &val, +			      NULL); +	if (rc < 0) +		return rc; + +	return val; +} + +static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, +				     bool an_enabled, bool an_master) +{ +	u16 ac = SJA1105_AC_AUTONEG_MODE_SGMII; + +	/* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to +	 * stop the clock during LPI mode, make the MAC reconfigure +	 * autonomously after PCS autoneg is done, flush the internal FIFOs. +	 */ +	sja1105_sgmii_write(priv, SJA1105_DC1, SJA1105_DC1_EN_VSMMD1 | +					       SJA1105_DC1_CLOCK_STOP_EN | +					       SJA1105_DC1_MAC_AUTO_SW | +					       SJA1105_DC1_INIT); +	/* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */ +	sja1105_sgmii_write(priv, SJA1105_DC2, SJA1105_DC2_TX_POL_INV_DISABLE); +	/* AUTONEG_CONTROL: Use SGMII autoneg */ +	if (an_master) +		ac |= SJA1105_AC_PHY_MODE | SJA1105_AC_SGMII_LINK; +	sja1105_sgmii_write(priv, SJA1105_AC, ac); +	/* BASIC_CONTROL: enable in-band AN now, if requested. Otherwise, +	 * sja1105_sgmii_pcs_force_speed must be called later for the link +	 * to become operational. +	 */ +	if (an_enabled) +		sja1105_sgmii_write(priv, MII_BMCR, +				    BMCR_ANENABLE | BMCR_ANRESTART); +} + +static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv, +					  int speed) +{ +	int pcs_speed; + +	switch (speed) { +	case SPEED_1000: +		pcs_speed = BMCR_SPEED1000; +		break; +	case SPEED_100: +		pcs_speed = BMCR_SPEED100; +		break; +	case SPEED_10: +		pcs_speed = BMCR_SPEED10; +		break; +	default: +		dev_err(priv->ds->dev, "Invalid speed %d\n", speed); +		return; +	} +	sja1105_sgmii_write(priv, MII_BMCR, pcs_speed | BMCR_FULLDPLX); +} +  /* Convert link speed from SJA1105 to ethtool encoding */  static int sja1105_speed[] = {  	[SJA1105_SPEED_AUTO]		= SPEED_UNKNOWN, @@ -704,8 +898,13 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,  	 * table, since this will be used for the clocking setup, and we no  	 * longer need to store it in the static config (already told hardware  	 * we want auto during upload phase). +	 * Actually for the SGMII port, the MAC is fixed at 1 Gbps and +	 * we need to configure the PCS only (if even that).  	 */ -	mac[port].speed = speed; +	if (sja1105_supports_sgmii(priv, port)) +		mac[port].speed = SJA1105_SPEED_1000MBPS; +	else +		mac[port].speed = speed;  	/* Write to the dynamic reconfiguration tables */  	rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, @@ -754,26 +953,34 @@ static bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port,  	case PHY_INTERFACE_MODE_RGMII_RXID:  	case PHY_INTERFACE_MODE_RGMII_TXID:  		return (phy_mode != XMII_MODE_RGMII); +	case PHY_INTERFACE_MODE_SGMII: +		return (phy_mode != XMII_MODE_SGMII);  	default:  		return true;  	}  }  static void sja1105_mac_config(struct dsa_switch *ds, int port, -			       unsigned int link_an_mode, +			       unsigned int mode,  			       const struct phylink_link_state *state)  {  	struct sja1105_private *priv = ds->priv; +	bool is_sgmii = sja1105_supports_sgmii(priv, port); -	if (sja1105_phy_mode_mismatch(priv, port, state->interface)) +	if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { +		dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", +			phy_modes(state->interface));  		return; +	} -	if (link_an_mode == MLO_AN_INBAND) { +	if (phylink_autoneg_inband(mode) && !is_sgmii) {  		dev_err(ds->dev, "In-band AN not supported!\n");  		return;  	} -	sja1105_adjust_port_config(priv, port, state->speed); +	if (is_sgmii) +		sja1105_sgmii_pcs_config(priv, phylink_autoneg_inband(mode), +					 false);  }  static void sja1105_mac_link_down(struct dsa_switch *ds, int port, @@ -786,9 +993,18 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port,  static void sja1105_mac_link_up(struct dsa_switch *ds, int port,  				unsigned int mode,  				phy_interface_t interface, -				struct phy_device *phydev) +				struct phy_device *phydev, +				int speed, int duplex, +				bool tx_pause, bool rx_pause)  { -	sja1105_inhibit_tx(ds->priv, BIT(port), false); +	struct sja1105_private *priv = ds->priv; + +	sja1105_adjust_port_config(priv, port, speed); + +	if (sja1105_supports_sgmii(priv, port) && !phylink_autoneg_inband(mode)) +		sja1105_sgmii_pcs_force_speed(priv, speed); + +	sja1105_inhibit_tx(priv, BIT(port), false);  }  static void sja1105_phylink_validate(struct dsa_switch *ds, int port, @@ -822,7 +1038,9 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port,  	phylink_set(mask, MII);  	phylink_set(mask, 10baseT_Full);  	phylink_set(mask, 100baseT_Full); -	if (mii->xmii_mode[port] == XMII_MODE_RGMII) +	phylink_set(mask, 100baseT1_Full); +	if (mii->xmii_mode[port] == XMII_MODE_RGMII || +	    mii->xmii_mode[port] == XMII_MODE_SGMII)  		phylink_set(mask, 1000baseT_Full);  	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); @@ -830,6 +1048,38 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port,  		   __ETHTOOL_LINK_MODE_MASK_NBITS);  } +static int sja1105_mac_pcs_get_state(struct dsa_switch *ds, int port, +				     struct phylink_link_state *state) +{ +	struct sja1105_private *priv = ds->priv; +	int ais; + +	/* Read the vendor-specific AUTONEG_INTR_STATUS register */ +	ais = sja1105_sgmii_read(priv, SJA1105_AIS); +	if (ais < 0) +		return ais; + +	switch (SJA1105_AIS_SPEED(ais)) { +	case 0: +		state->speed = SPEED_10; +		break; +	case 1: +		state->speed = SPEED_100; +		break; +	case 2: +		state->speed = SPEED_1000; +		break; +	default: +		dev_err(ds->dev, "Invalid SGMII PCS speed %lu\n", +			SJA1105_AIS_SPEED(ais)); +	} +	state->duplex = SJA1105_AIS_DUPLEX_MODE(ais); +	state->an_complete = SJA1105_AIS_COMPLETE(ais); +	state->link = SJA1105_AIS_LINK_STATUS(ais); + +	return 0; +} +  static int  sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port,  			      const struct sja1105_l2_lookup_entry *requested) @@ -1338,6 +1588,7 @@ static const char * const sja1105_reset_reasons[] = {  	[SJA1105_RX_HWTSTAMPING] = "RX timestamping",  	[SJA1105_AGEING_TIME] = "Ageing time",  	[SJA1105_SCHEDULING] = "Time-aware scheduling", +	[SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing",  };  /* For situations where we need to change a setting at runtime that is only @@ -1356,6 +1607,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv,  	struct dsa_switch *ds = priv->ds;  	s64 t1, t2, t3, t4;  	s64 t12, t34; +	u16 bmcr = 0;  	int rc, i;  	s64 now; @@ -1373,6 +1625,9 @@ int sja1105_static_config_reload(struct sja1105_private *priv,  		mac[i].speed = SJA1105_SPEED_AUTO;  	} +	if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) +		bmcr = sja1105_sgmii_read(priv, MII_BMCR); +  	/* No PTP operations can run right now */  	mutex_lock(&priv->ptp_data.lock); @@ -1422,6 +1677,25 @@ out_unlock_ptp:  		if (rc < 0)  			goto out;  	} + +	if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) { +		bool an_enabled = !!(bmcr & BMCR_ANENABLE); + +		sja1105_sgmii_pcs_config(priv, an_enabled, false); + +		if (!an_enabled) { +			int speed = SPEED_UNKNOWN; + +			if (bmcr & BMCR_SPEED1000) +				speed = SPEED_1000; +			else if (bmcr & BMCR_SPEED100) +				speed = SPEED_100; +			else if (bmcr & BMCR_SPEED10) +				speed = SPEED_10; + +			sja1105_sgmii_pcs_force_speed(priv, speed); +		} +	}  out:  	mutex_unlock(&priv->mgmt_lock); @@ -1723,6 +1997,8 @@ static int sja1105_setup(struct dsa_switch *ds)  	/* Advertise the 8 egress queues */  	ds->num_tx_queues = SJA1105_NUM_TC; +	ds->mtu_enforcement_ingress = true; +  	/* The DSA/switchdev model brings up switch ports in standalone mode by  	 * default, and that means vlan_filtering is 0 since they're not under  	 * a bridge, so it's safe to set up switch tagging at this time. @@ -1745,6 +2021,7 @@ static void sja1105_teardown(struct dsa_switch *ds)  			kthread_destroy_worker(sp->xmit_worker);  	} +	sja1105_flower_teardown(ds);  	sja1105_tas_teardown(ds);  	sja1105_ptp_clock_unregister(ds);  	sja1105_static_config_free(&priv->static_config); @@ -1891,6 +2168,31 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds,  	return sja1105_static_config_reload(priv, SJA1105_AGEING_TIME);  } +static int sja1105_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ +	struct sja1105_l2_policing_entry *policing; +	struct sja1105_private *priv = ds->priv; + +	new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; + +	if (dsa_is_cpu_port(ds, port)) +		new_mtu += VLAN_HLEN; + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	if (policing[port].maxlen == new_mtu) +		return 0; + +	policing[port].maxlen = new_mtu; + +	return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static int sja1105_get_max_mtu(struct dsa_switch *ds, int port) +{ +	return 2043 - VLAN_ETH_HLEN - ETH_FCS_LEN; +} +  static int sja1105_port_setup_tc(struct dsa_switch *ds, int port,  				 enum tc_setup_type type,  				 void *type_data) @@ -1981,12 +2283,49 @@ static void sja1105_mirror_del(struct dsa_switch *ds, int port,  			     mirror->ingress, false);  } +static int sja1105_port_policer_add(struct dsa_switch *ds, int port, +				    struct dsa_mall_policer_tc_entry *policer) +{ +	struct sja1105_l2_policing_entry *policing; +	struct sja1105_private *priv = ds->priv; + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	/* In hardware, every 8 microseconds the credit level is incremented by +	 * the value of RATE bytes divided by 64, up to a maximum of SMAX +	 * bytes. +	 */ +	policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, +				      1000000); +	policing[port].smax = div_u64(policer->rate_bytes_per_sec * +				      PSCHED_NS2TICKS(policer->burst), +				      PSCHED_TICKS_PER_SEC); + +	return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static void sja1105_port_policer_del(struct dsa_switch *ds, int port) +{ +	struct sja1105_l2_policing_entry *policing; +	struct sja1105_private *priv = ds->priv; + +	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + +	policing[port].rate = SJA1105_RATE_MBPS(1000); +	policing[port].smax = 65535; + +	sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} +  static const struct dsa_switch_ops sja1105_switch_ops = {  	.get_tag_protocol	= sja1105_get_tag_protocol,  	.setup			= sja1105_setup,  	.teardown		= sja1105_teardown,  	.set_ageing_time	= sja1105_set_ageing_time, +	.port_change_mtu	= sja1105_change_mtu, +	.port_max_mtu		= sja1105_get_max_mtu,  	.phylink_validate	= sja1105_phylink_validate, +	.phylink_mac_link_state	= sja1105_mac_pcs_get_state,  	.phylink_mac_config	= sja1105_mac_config,  	.phylink_mac_link_up	= sja1105_mac_link_up,  	.phylink_mac_link_down	= sja1105_mac_link_down, @@ -2016,6 +2355,10 @@ static const struct dsa_switch_ops sja1105_switch_ops = {  	.port_setup_tc		= sja1105_port_setup_tc,  	.port_mirror_add	= sja1105_mirror_add,  	.port_mirror_del	= sja1105_mirror_del, +	.port_policer_add	= sja1105_port_policer_add, +	.port_policer_del	= sja1105_port_policer_del, +	.cls_flower_add		= sja1105_cls_flower_add, +	.cls_flower_del		= sja1105_cls_flower_del,  };  static int sja1105_check_device_id(struct sja1105_private *priv) @@ -2119,6 +2462,7 @@ static int sja1105_probe(struct spi_device *spi)  	mutex_init(&priv->mgmt_lock);  	sja1105_tas_setup(ds); +	sja1105_flower_setup(ds);  	rc = dsa_register_switch(priv->ds);  	if (rc) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index a836fc38c4a4..bc0e47c1dbb9 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -14,6 +14,18 @@  #define SJA1105_MAX_ADJ_PPB		32000000  #define SJA1105_SIZE_PTP_CMD		4 +/* PTPSYNCTS has no interrupt or update mechanism, because the intended + * hardware use case is for the timestamp to be collected synchronously, + * immediately after the CAS_MASTER SJA1105 switch has performed a CASSYNC + * one-shot toggle (no return to level) on the PTP_CLK pin. When used as a + * generic extts source, the PTPSYNCTS register needs polling and a comparison + * with the old value. The polling interval is configured as the Nyquist rate + * of a signal with 50% duty cycle and 1Hz frequency, which is sadly all that + * this hardware can do (but may be enough for some setups). Anything of higher + * frequency than 1 Hz will be lost, since there is no timestamp FIFO. + */ +#define SJA1105_EXTTS_INTERVAL		(HZ / 4) +  /*            This range is actually +/- SJA1105_MAX_ADJ_PPB   *            divided by 1000 (ppb -> ppm) and with a 16-bit   *            "fractional" part (actually fixed point). @@ -39,44 +51,13 @@ enum sja1105_ptp_clk_mode {  	PTP_SET_MODE = 0,  }; +#define extts_to_data(d) \ +		container_of((d), struct sja1105_ptp_data, extts_work)  #define ptp_caps_to_data(d) \  		container_of((d), struct sja1105_ptp_data, caps)  #define ptp_data_to_sja1105(d) \  		container_of((d), struct sja1105_private, ptp_data) -static int sja1105_init_avb_params(struct sja1105_private *priv, -				   bool on) -{ -	struct sja1105_avb_params_entry *avb; -	struct sja1105_table *table; - -	table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS]; - -	/* Discard previous AVB Parameters Table */ -	if (table->entry_count) { -		kfree(table->entries); -		table->entry_count = 0; -	} - -	/* Configure the reception of meta frames only if requested */ -	if (!on) -		return 0; - -	table->entries = kcalloc(SJA1105_MAX_AVB_PARAMS_COUNT, -				 table->ops->unpacked_entry_size, GFP_KERNEL); -	if (!table->entries) -		return -ENOMEM; - -	table->entry_count = SJA1105_MAX_AVB_PARAMS_COUNT; - -	avb = table->entries; - -	avb->destmeta = SJA1105_META_DMAC; -	avb->srcmeta  = SJA1105_META_SMAC; - -	return 0; -} -  /* Must be called only with priv->tagger_data.state bit   * SJA1105_HWTS_RX_EN cleared   */ @@ -86,17 +67,12 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	struct sja1105_general_params_entry *general_params;  	struct sja1105_table *table; -	int rc;  	table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];  	general_params = table->entries;  	general_params->send_meta1 = on;  	general_params->send_meta0 = on; -	rc = sja1105_init_avb_params(priv, on); -	if (rc < 0) -		return rc; -  	/* Initialize the meta state machine to a known state */  	if (priv->tagger_data.stampable_skb) {  		kfree_skb(priv->tagger_data.stampable_skb); @@ -206,6 +182,8 @@ void sja1105et_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,  	sja1105_packing(buf, &valid,           31, 31, size, op);  	sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);  	sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op); +	sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op); +	sja1105_packing(buf, &cmd->stopptpcp,  27, 27, size, op);  	sja1105_packing(buf, &cmd->resptp,      2,  2, size, op);  	sja1105_packing(buf, &cmd->corrclk4ts,  1,  1, size, op);  	sja1105_packing(buf, &cmd->ptpclkadd,   0,  0, size, op); @@ -221,6 +199,8 @@ void sja1105pqrs_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,  	sja1105_packing(buf, &valid,           31, 31, size, op);  	sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);  	sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op); +	sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op); +	sja1105_packing(buf, &cmd->stopptpcp,  27, 27, size, op);  	sja1105_packing(buf, &cmd->resptp,      3,  3, size, op);  	sja1105_packing(buf, &cmd->corrclk4ts,  2,  2, size, op);  	sja1105_packing(buf, &cmd->ptpclkadd,   0,  0, size, op); @@ -615,6 +595,236 @@ static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)  	return rc;  } +static void sja1105_ptp_extts_work(struct work_struct *work) +{ +	struct delayed_work *dw = to_delayed_work(work); +	struct sja1105_ptp_data *ptp_data = extts_to_data(dw); +	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); +	const struct sja1105_regs *regs = priv->info->regs; +	struct ptp_clock_event event; +	u64 ptpsyncts = 0; +	int rc; + +	mutex_lock(&ptp_data->lock); + +	rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts, +			      NULL); +	if (rc < 0) +		dev_err_ratelimited(priv->ds->dev, +				    "Failed to read PTPSYNCTS: %d\n", rc); + +	if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) { +		event.index = 0; +		event.type = PTP_CLOCK_EXTTS; +		event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts)); +		ptp_clock_event(ptp_data->clock, &event); + +		ptp_data->ptpsyncts = ptpsyncts; +	} + +	mutex_unlock(&ptp_data->lock); + +	schedule_delayed_work(&ptp_data->extts_work, SJA1105_EXTTS_INTERVAL); +} + +static int sja1105_change_ptp_clk_pin_func(struct sja1105_private *priv, +					   enum ptp_pin_function func) +{ +	struct sja1105_avb_params_entry *avb; +	enum ptp_pin_function old_func; + +	avb = priv->static_config.tables[BLK_IDX_AVB_PARAMS].entries; + +	if (priv->info->device_id == SJA1105E_DEVICE_ID || +	    priv->info->device_id == SJA1105T_DEVICE_ID || +	    avb->cas_master) +		old_func = PTP_PF_PEROUT; +	else +		old_func = PTP_PF_EXTTS; + +	if (func == old_func) +		return 0; + +	avb->cas_master = (func == PTP_PF_PEROUT); + +	return sja1105_dynamic_config_write(priv, BLK_IDX_AVB_PARAMS, 0, avb, +					    true); +} + +/* The PTP_CLK pin may be configured to toggle with a 50% duty cycle and a + * frequency f: + * + *           NSEC_PER_SEC + * f = ---------------------- + *     (PTPPINDUR * 8 ns) * 2 + */ +static int sja1105_per_out_enable(struct sja1105_private *priv, +				  struct ptp_perout_request *perout, +				  bool on) +{ +	struct sja1105_ptp_data *ptp_data = &priv->ptp_data; +	const struct sja1105_regs *regs = priv->info->regs; +	struct sja1105_ptp_cmd cmd = ptp_data->cmd; +	int rc; + +	/* We only support one channel */ +	if (perout->index != 0) +		return -EOPNOTSUPP; + +	/* Reject requests with unsupported flags */ +	if (perout->flags) +		return -EOPNOTSUPP; + +	mutex_lock(&ptp_data->lock); + +	rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_PEROUT); +	if (rc) +		goto out; + +	if (on) { +		struct timespec64 pin_duration_ts = { +			.tv_sec = perout->period.sec, +			.tv_nsec = perout->period.nsec, +		}; +		struct timespec64 pin_start_ts = { +			.tv_sec = perout->start.sec, +			.tv_nsec = perout->start.nsec, +		}; +		u64 pin_duration = timespec64_to_ns(&pin_duration_ts); +		u64 pin_start = timespec64_to_ns(&pin_start_ts); +		u32 pin_duration32; +		u64 now; + +		/* ptppindur: 32 bit register which holds the interval between +		 * 2 edges on PTP_CLK. So check for truncation which happens +		 * at periods larger than around 68.7 seconds. +		 */ +		pin_duration = ns_to_sja1105_ticks(pin_duration / 2); +		if (pin_duration > U32_MAX) { +			rc = -ERANGE; +			goto out; +		} +		pin_duration32 = pin_duration; + +		/* ptppins: 64 bit register which needs to hold a PTP time +		 * larger than the current time, otherwise the startptpcp +		 * command won't do anything. So advance the current time +		 * by a number of periods in a way that won't alter the +		 * phase offset. +		 */ +		rc = __sja1105_ptp_gettimex(priv->ds, &now, NULL); +		if (rc < 0) +			goto out; + +		pin_start = future_base_time(pin_start, pin_duration, +					     now + 1ull * NSEC_PER_SEC); +		pin_start = ns_to_sja1105_ticks(pin_start); + +		rc = sja1105_xfer_u64(priv, SPI_WRITE, regs->ptppinst, +				      &pin_start, NULL); +		if (rc < 0) +			goto out; + +		rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptppindur, +				      &pin_duration32, NULL); +		if (rc < 0) +			goto out; +	} + +	if (on) +		cmd.startptpcp = true; +	else +		cmd.stopptpcp = true; + +	rc = sja1105_ptp_commit(priv->ds, &cmd, SPI_WRITE); + +out: +	mutex_unlock(&ptp_data->lock); + +	return rc; +} + +static int sja1105_extts_enable(struct sja1105_private *priv, +				struct ptp_extts_request *extts, +				bool on) +{ +	int rc; + +	/* We only support one channel */ +	if (extts->index != 0) +		return -EOPNOTSUPP; + +	/* Reject requests with unsupported flags */ +	if (extts->flags & ~(PTP_ENABLE_FEATURE | +			     PTP_RISING_EDGE | +			     PTP_FALLING_EDGE | +			     PTP_STRICT_FLAGS)) +		return -EOPNOTSUPP; + +	/* We can only enable time stamping on both edges, sadly. */ +	if ((extts->flags & PTP_STRICT_FLAGS) && +	    (extts->flags & PTP_ENABLE_FEATURE) && +	    (extts->flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES) +		return -EOPNOTSUPP; + +	rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_EXTTS); +	if (rc) +		return rc; + +	if (on) +		schedule_delayed_work(&priv->ptp_data.extts_work, +				      SJA1105_EXTTS_INTERVAL); +	else +		cancel_delayed_work_sync(&priv->ptp_data.extts_work); + +	return 0; +} + +static int sja1105_ptp_enable(struct ptp_clock_info *ptp, +			      struct ptp_clock_request *req, int on) +{ +	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); +	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); +	int rc = -EOPNOTSUPP; + +	if (req->type == PTP_CLK_REQ_PEROUT) +		rc = sja1105_per_out_enable(priv, &req->perout, on); +	else if (req->type == PTP_CLK_REQ_EXTTS) +		rc = sja1105_extts_enable(priv, &req->extts, on); + +	return rc; +} + +static int sja1105_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, +				  enum ptp_pin_function func, unsigned int chan) +{ +	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); +	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); + +	if (chan != 0 || pin != 0) +		return -1; + +	switch (func) { +	case PTP_PF_NONE: +	case PTP_PF_PEROUT: +		break; +	case PTP_PF_EXTTS: +		if (priv->info->device_id == SJA1105E_DEVICE_ID || +		    priv->info->device_id == SJA1105T_DEVICE_ID) +			return -1; +		break; +	default: +		return -1; +	} +	return 0; +} + +static struct ptp_pin_desc sja1105_ptp_pin = { +	.name = "ptp_clk", +	.index = 0, +	.func = PTP_PF_NONE, +}; +  int sja1105_ptp_clock_register(struct dsa_switch *ds)  {  	struct sja1105_private *priv = ds->priv; @@ -628,8 +838,14 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  		.adjtime	= sja1105_ptp_adjtime,  		.gettimex64	= sja1105_ptp_gettimex,  		.settime64	= sja1105_ptp_settime, +		.enable		= sja1105_ptp_enable, +		.verify		= sja1105_ptp_verify_pin,  		.do_aux_work	= sja1105_rxtstamp_work,  		.max_adj	= SJA1105_MAX_ADJ_PPB, +		.pin_config	= &sja1105_ptp_pin, +		.n_pins		= 1, +		.n_ext_ts	= 1, +		.n_per_out	= 1,  	};  	skb_queue_head_init(&ptp_data->skb_rxtstamp_queue); @@ -642,6 +858,8 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  	ptp_data->cmd.corrclk4ts = true;  	ptp_data->cmd.ptpclkadd = PTP_SET_MODE; +	INIT_DELAYED_WORK(&ptp_data->extts_work, sja1105_ptp_extts_work); +  	return sja1105_ptp_reset(ds);  } @@ -653,6 +871,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds)  	if (IS_ERR_OR_NULL(ptp_data->clock))  		return; +	cancel_delayed_work_sync(&ptp_data->extts_work);  	ptp_cancel_worker_sync(ptp_data->clock);  	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);  	ptp_clock_unregister(ptp_data->clock); diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 6f4a19eec709..43480b24f1f0 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -21,7 +21,36 @@ static inline s64 sja1105_ticks_to_ns(s64 ticks)  	return ticks * SJA1105_TICK_NS;  } +/* Calculate the first base_time in the future that satisfies this + * relationship: + * + * future_base_time = base_time + N x cycle_time >= now, or + * + *      now - base_time + * N >= --------------- + *         cycle_time + * + * Because N is an integer, the ceiling value of the above "a / b" ratio + * is in fact precisely the floor value of "(a + b - 1) / b", which is + * easier to calculate only having integer division tools. + */ +static inline s64 future_base_time(s64 base_time, s64 cycle_time, s64 now) +{ +	s64 a, b, n; + +	if (base_time >= now) +		return base_time; + +	a = now - base_time; +	b = cycle_time; +	n = div_s64(a + b - 1, b); + +	return base_time + n * cycle_time; +} +  struct sja1105_ptp_cmd { +	u64 startptpcp;		/* start toggling PTP_CLK pin */ +	u64 stopptpcp;		/* stop toggling PTP_CLK pin */  	u64 ptpstrtsch;		/* start schedule */  	u64 ptpstopsch;		/* stop schedule */  	u64 resptp;		/* reset */ @@ -30,12 +59,14 @@ struct sja1105_ptp_cmd {  };  struct sja1105_ptp_data { +	struct delayed_work extts_work;  	struct sk_buff_head skb_rxtstamp_queue;  	struct ptp_clock_info caps;  	struct ptp_clock *clock;  	struct sja1105_ptp_cmd cmd;  	/* Serializes all operations on the PTP hardware clock */  	struct mutex lock; +	u64 ptpsyncts;  };  int sja1105_ptp_clock_register(struct dsa_switch *ds); diff --git a/drivers/net/dsa/sja1105/sja1105_sgmii.h b/drivers/net/dsa/sja1105/sja1105_sgmii.h new file mode 100644 index 000000000000..24d9bc046e70 --- /dev/null +++ b/drivers/net/dsa/sja1105/sja1105_sgmii.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright 2020, NXP Semiconductors + */ +#ifndef _SJA1105_SGMII_H +#define _SJA1105_SGMII_H + +#define SJA1105_SGMII_PORT		4 + +/* DIGITAL_CONTROL_1 (address 1f8000h) */ +#define SJA1105_DC1			0x8000 +#define SJA1105_DC1_VS_RESET		BIT(15) +#define SJA1105_DC1_REMOTE_LOOPBACK	BIT(14) +#define SJA1105_DC1_EN_VSMMD1		BIT(13) +#define SJA1105_DC1_POWER_SAVE		BIT(11) +#define SJA1105_DC1_CLOCK_STOP_EN	BIT(10) +#define SJA1105_DC1_MAC_AUTO_SW		BIT(9) +#define SJA1105_DC1_INIT		BIT(8) +#define SJA1105_DC1_TX_DISABLE		BIT(4) +#define SJA1105_DC1_AUTONEG_TIMER_OVRR	BIT(3) +#define SJA1105_DC1_BYP_POWERUP		BIT(1) +#define SJA1105_DC1_PHY_MODE_CONTROL	BIT(0) + +/* DIGITAL_CONTROL_2 register (address 1f80E1h) */ +#define SJA1105_DC2			0x80e1 +#define SJA1105_DC2_TX_POL_INV_DISABLE	BIT(4) +#define SJA1105_DC2_RX_POL_INV		BIT(0) + +/* DIGITAL_ERROR_CNT register (address 1f80E2h) */ +#define SJA1105_DEC			0x80e2 +#define SJA1105_DEC_ICG_EC_ENA		BIT(4) +#define SJA1105_DEC_CLEAR_ON_READ	BIT(0) + +/* AUTONEG_CONTROL register (address 1f8001h) */ +#define SJA1105_AC			0x8001 +#define SJA1105_AC_MII_CONTROL		BIT(8) +#define SJA1105_AC_SGMII_LINK		BIT(4) +#define SJA1105_AC_PHY_MODE		BIT(3) +#define SJA1105_AC_AUTONEG_MODE(x)	(((x) << 1) & GENMASK(2, 1)) +#define SJA1105_AC_AUTONEG_MODE_SGMII	SJA1105_AC_AUTONEG_MODE(2) + +/* AUTONEG_INTR_STATUS register (address 1f8002h) */ +#define SJA1105_AIS			0x8002 +#define SJA1105_AIS_LINK_STATUS(x)	(!!((x) & BIT(4))) +#define SJA1105_AIS_SPEED(x)		(((x) & GENMASK(3, 2)) >> 2) +#define SJA1105_AIS_DUPLEX_MODE(x)	(!!((x) & BIT(1))) +#define SJA1105_AIS_COMPLETE(x)		(!!((x) & BIT(0))) + +/* DEBUG_CONTROL register (address 1f8005h) */ +#define SJA1105_DC			0x8005 +#define SJA1105_DC_SUPPRESS_LOS		BIT(4) +#define SJA1105_DC_RESTART_SYNC		BIT(0) + +#endif diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 29b127f3bf9c..04bdb72ae6b6 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -458,6 +458,8 @@ static struct sja1105_regs sja1105et_regs = {  	.rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},  	.ptpegr_ts = {0xC0, 0xC2, 0xC4, 0xC6, 0xC8},  	.ptpschtm = 0x12, /* Spans 0x12 to 0x13 */ +	.ptppinst = 0x14, +	.ptppindur = 0x16,  	.ptp_control = 0x17,  	.ptpclkval = 0x18, /* Spans 0x18 to 0x19 */  	.ptpclkrate = 0x1A, @@ -474,11 +476,13 @@ static struct sja1105_regs sja1105pqrs_regs = {  	/* UM10944.pdf, Table 86, ACU Register overview */  	.pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},  	.pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, +	.sgmii = 0x1F0000,  	.rmii_pll1 = 0x10000A,  	.cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},  	.mac = {0x200, 0x202, 0x204, 0x206, 0x208},  	.mac_hl1 = {0x400, 0x410, 0x420, 0x430, 0x440},  	.mac_hl2 = {0x600, 0x610, 0x620, 0x630, 0x640}, +	.ether_stats = {0x1400, 0x1418, 0x1430, 0x1448, 0x1460},  	/* UM11040.pdf, Table 114 */  	.mii_tx_clk = {0x100013, 0x100019, 0x10001F, 0x100025, 0x10002B},  	.mii_rx_clk = {0x100014, 0x10001A, 0x100020, 0x100026, 0x10002C}, @@ -490,10 +494,13 @@ static struct sja1105_regs sja1105pqrs_regs = {  	.qlevel = {0x604, 0x614, 0x624, 0x634, 0x644},  	.ptpegr_ts = {0xC0, 0xC4, 0xC8, 0xCC, 0xD0},  	.ptpschtm = 0x13, /* Spans 0x13 to 0x14 */ +	.ptppinst = 0x15, +	.ptppindur = 0x17,  	.ptp_control = 0x18,  	.ptpclkval = 0x19,  	.ptpclkrate = 0x1B,  	.ptpclkcorp = 0x1E, +	.ptpsyncts = 0x1F,  };  struct sja1105_info sja1105e_info = { diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index 63d2311817c4..bbfe034910a0 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -102,12 +102,13 @@ static size_t sja1105et_avb_params_entry_packing(void *buf, void *entry_ptr,  	return size;  } -static size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr, -						   enum packing_op op) +size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr, +					    enum packing_op op)  {  	const size_t size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY;  	struct sja1105_avb_params_entry *entry = entry_ptr; +	sja1105_packing(buf, &entry->cas_master, 126, 126, size, op);  	sja1105_packing(buf, &entry->destmeta,   125,  78, size, op);  	sja1105_packing(buf, &entry->srcmeta,     77,  30, size, op);  	return size; diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h index f4a5c5c04311..8afafb6aef12 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.h +++ b/drivers/net/dsa/sja1105/sja1105_static_config.h @@ -230,6 +230,7 @@ struct sja1105_l2_policing_entry {  };  struct sja1105_avb_params_entry { +	u64 cas_master;  	u64 destmeta;  	u64 srcmeta;  }; diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c index fa6750d973d7..77e547b4cd89 100644 --- a/drivers/net/dsa/sja1105/sja1105_tas.c +++ b/drivers/net/dsa/sja1105/sja1105_tas.c @@ -28,33 +28,6 @@ static s64 sja1105_delta_to_ns(s64 delta)  	return delta * 200;  } -/* Calculate the first base_time in the future that satisfies this - * relationship: - * - * future_base_time = base_time + N x cycle_time >= now, or - * - *      now - base_time - * N >= --------------- - *         cycle_time - * - * Because N is an integer, the ceiling value of the above "a / b" ratio - * is in fact precisely the floor value of "(a + b - 1) / b", which is - * easier to calculate only having integer division tools. - */ -static s64 future_base_time(s64 base_time, s64 cycle_time, s64 now) -{ -	s64 a, b, n; - -	if (base_time >= now) -		return base_time; - -	a = now - base_time; -	b = cycle_time; -	n = div_s64(a + b - 1, b); - -	return base_time + n * cycle_time; -} -  static int sja1105_tas_set_runtime_params(struct sja1105_private *priv)  {  	struct sja1105_tas_data *tas_data = &priv->tas_data; diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 6e21a2a5cf01..19ce4aa0973b 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -664,16 +664,6 @@ static void vsc73xx_init_port(struct vsc73xx *vsc, int port)  		      VSC73XX_MAC_CFG_TX_EN |  		      VSC73XX_MAC_CFG_RX_EN); -	/* Max length, we can do up to 9.6 KiB, so allow that. -	 * According to application not "VSC7398 Jumbo Frames" setting -	 * up the MTU to 9.6 KB does not affect the performance on standard -	 * frames, so just enable it. It is clear from the application note -	 * that "9.6 kilobytes" == 9600 bytes. -	 */ -	vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, -		      port, -		      VSC73XX_MAXLEN, 9600); -  	/* Flow control for the CPU port:  	 * Use a zero delay pause frame when pause condition is left  	 * Obey pause control frames @@ -1030,6 +1020,24 @@ static void vsc73xx_get_ethtool_stats(struct dsa_switch *ds, int port,  	}  } +static int vsc73xx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ +	struct vsc73xx *vsc = ds->priv; + +	return vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, +			     VSC73XX_MAXLEN, new_mtu); +} + +/* According to application not "VSC7398 Jumbo Frames" setting + * up the MTU to 9.6 KB does not affect the performance on standard + * frames. It is clear from the application note that + * "9.6 kilobytes" == 9600 bytes. + */ +static int vsc73xx_get_max_mtu(struct dsa_switch *ds, int port) +{ +	return 9600; +} +  static const struct dsa_switch_ops vsc73xx_ds_ops = {  	.get_tag_protocol = vsc73xx_get_tag_protocol,  	.setup = vsc73xx_setup, @@ -1041,6 +1049,8 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {  	.get_sset_count = vsc73xx_get_sset_count,  	.port_enable = vsc73xx_port_enable,  	.port_disable = vsc73xx_port_disable, +	.port_change_mtu = vsc73xx_change_mtu, +	.port_max_mtu = vsc73xx_get_max_mtu,  };  static int vsc73xx_gpio_get(struct gpio_chip *chip, unsigned int offset) |