diff options
Diffstat (limited to 'drivers/net/ethernet/mscc')
| -rw-r--r-- | drivers/net/ethernet/mscc/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 188 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot_flower.c | 5 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot_mrp.c | 225 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot_net.c | 234 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot_ptp.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/mscc/ocelot_vcap.c | 1 | 
7 files changed, 470 insertions, 188 deletions
diff --git a/drivers/net/ethernet/mscc/Kconfig b/drivers/net/ethernet/mscc/Kconfig index 05cb040c2677..2d3157e4d081 100644 --- a/drivers/net/ethernet/mscc/Kconfig +++ b/drivers/net/ethernet/mscc/Kconfig @@ -11,7 +11,7 @@ config NET_VENDOR_MICROSEMI  if NET_VENDOR_MICROSEMI -# Users should depend on NET_SWITCHDEV, HAS_IOMEM +# Users should depend on NET_SWITCHDEV, HAS_IOMEM, BRIDGE  config MSCC_OCELOT_SWITCH_LIB  	select NET_DEVLINK  	select REGMAP_MMIO @@ -24,6 +24,7 @@ config MSCC_OCELOT_SWITCH_LIB  config MSCC_OCELOT_SWITCH  	tristate "Ocelot switch driver" +	depends on BRIDGE || BRIDGE=n  	depends on NET_SWITCHDEV  	depends on HAS_IOMEM  	depends on OF_NET diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 46e5c9136bac..0c4283319d7f 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -6,6 +6,7 @@   */  #include <linux/dsa/ocelot.h>  #include <linux/if_bridge.h> +#include <linux/ptp_classify.h>  #include <soc/mscc/ocelot_vcap.h>  #include "ocelot.h"  #include "ocelot_vcap.h" @@ -484,7 +485,8 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,  			   DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);  	/* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of -	 * reset */ +	 * reset +	 */  	ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed),  			   DEV_CLOCK_CFG); @@ -529,22 +531,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)  }  EXPORT_SYMBOL(ocelot_port_disable); -void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, -				  struct sk_buff *clone) +static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, +					 struct sk_buff *clone)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port];  	spin_lock(&ocelot_port->ts_id_lock);  	skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; -	/* Store timestamp ID in cb[0] of sk_buff */ -	clone->cb[0] = ocelot_port->ts_id; +	/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ +	OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;  	ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;  	skb_queue_tail(&ocelot_port->tx_skbs, clone);  	spin_unlock(&ocelot_port->ts_id_lock);  } -EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); + +u32 ocelot_ptp_rew_op(struct sk_buff *skb) +{ +	struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone; +	u8 ptp_cmd = OCELOT_SKB_CB(skb)->ptp_cmd; +	u32 rew_op = 0; + +	if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP && clone) { +		rew_op = ptp_cmd; +		rew_op |= OCELOT_SKB_CB(clone)->ts_id << 3; +	} else if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { +		rew_op = ptp_cmd; +	} + +	return rew_op; +} +EXPORT_SYMBOL(ocelot_ptp_rew_op); + +static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb) +{ +	struct ptp_header *hdr; +	unsigned int ptp_class; +	u8 msgtype, twostep; + +	ptp_class = ptp_classify_raw(skb); +	if (ptp_class == PTP_CLASS_NONE) +		return false; + +	hdr = ptp_parse_header(skb, ptp_class); +	if (!hdr) +		return false; + +	msgtype = ptp_get_msgtype(hdr, ptp_class); +	twostep = hdr->flag_field[0] & 0x2; + +	if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0) +		return true; + +	return false; +} + +int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port, +				 struct sk_buff *skb, +				 struct sk_buff **clone) +{ +	struct ocelot_port *ocelot_port = ocelot->ports[port]; +	u8 ptp_cmd = ocelot_port->ptp_cmd; + +	/* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */ +	if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { +		if (ocelot_ptp_is_onestep_sync(skb)) { +			OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; +			return 0; +		} + +		/* Fall back to two-step timestamping */ +		ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; +	} + +	if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { +		*clone = skb_clone_sk(skb); +		if (!(*clone)) +			return -ENOMEM; + +		ocelot_port_add_txtstamp_skb(ocelot, port, *clone); +		OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; +	} + +	return 0; +} +EXPORT_SYMBOL(ocelot_port_txtstamp_request);  static void ocelot_get_hwtimestamp(struct ocelot *ocelot,  				   struct timespec64 *ts) @@ -603,7 +675,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)  		spin_lock_irqsave(&port->tx_skbs.lock, flags);  		skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { -			if (skb->cb[0] != id) +			if (OCELOT_SKB_CB(skb)->ts_id != id)  				continue;  			__skb_unlink(skb, &port->tx_skbs);  			skb_match = skb; @@ -687,7 +759,7 @@ static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)  int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)  {  	struct skb_shared_hwtstamps *shhwtstamps; -	u64 tod_in_ns, full_ts_in_ns, cpuq; +	u64 tod_in_ns, full_ts_in_ns;  	u64 timestamp, src_port, len;  	u32 xfh[OCELOT_TAG_LEN / 4];  	struct net_device *dev; @@ -704,7 +776,6 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)  	ocelot_xfh_get_src_port(xfh, &src_port);  	ocelot_xfh_get_len(xfh, &len);  	ocelot_xfh_get_rew_val(xfh, ×tamp); -	ocelot_xfh_get_cpuq(xfh, &cpuq);  	if (WARN_ON(src_port >= ocelot->num_phys_ports))  		return -EINVAL; @@ -767,17 +838,11 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)  	/* Everything we see on an interface that is in the HW bridge  	 * has already been forwarded.  	 */ -	if (ocelot->bridge_mask & BIT(src_port)) +	if (ocelot->ports[src_port]->bridge)  		skb->offload_fwd_mark = 1;  	skb->protocol = eth_type_trans(skb, dev); -#if IS_ENABLED(CONFIG_BRIDGE_MRP) -	if (skb->protocol == cpu_to_be16(ETH_P_MRP) && -	    cpuq & BIT(OCELOT_MRP_CPUQ)) -		skb->offload_fwd_mark = 0; -#endif -  	*nskb = skb;  	return 0; @@ -1190,6 +1255,26 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond,  	return mask;  } +static u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, +				      struct net_device *bridge) +{ +	u32 mask = 0; +	int port; + +	for (port = 0; port < ocelot->num_phys_ports; port++) { +		struct ocelot_port *ocelot_port = ocelot->ports[port]; + +		if (!ocelot_port) +			continue; + +		if (ocelot_port->stp_state == BR_STATE_FORWARDING && +		    ocelot_port->bridge == bridge) +			mask |= BIT(port); +	} + +	return mask; +} +  static u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot)  {  	u32 mask = 0; @@ -1239,10 +1324,12 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)  			 */  			mask = GENMASK(ocelot->num_phys_ports - 1, 0);  			mask &= ~cpu_fwd_mask; -		} else if (ocelot->bridge_fwd_mask & BIT(port)) { +		} else if (ocelot_port->bridge) { +			struct net_device *bridge = ocelot_port->bridge;  			struct net_device *bond = ocelot_port->bond; -			mask = ocelot->bridge_fwd_mask & ~BIT(port); +			mask = ocelot_get_bridge_fwd_mask(ocelot, bridge); +			mask &= ~BIT(port);  			if (bond) {  				mask &= ~ocelot_get_bond_mask(ocelot, bond,  							      false); @@ -1263,29 +1350,16 @@ EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);  void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	u32 port_cfg; +	u32 learn_ena = 0; -	if (!(BIT(port) & ocelot->bridge_mask)) -		return; +	ocelot_port->stp_state = state; -	port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port); +	if ((state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) && +	    ocelot_port->learn_ena) +		learn_ena = ANA_PORT_PORT_CFG_LEARN_ENA; -	switch (state) { -	case BR_STATE_FORWARDING: -		ocelot->bridge_fwd_mask |= BIT(port); -		fallthrough; -	case BR_STATE_LEARNING: -		if (ocelot_port->learn_ena) -			port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA; -		break; - -	default: -		port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA; -		ocelot->bridge_fwd_mask &= ~BIT(port); -		break; -	} - -	ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port); +	ocelot_rmw_gix(ocelot, learn_ena, ANA_PORT_PORT_CFG_LEARN_ENA, +		       ANA_PORT_PORT_CFG, port);  	ocelot_apply_bridge_fwd_mask(ocelot);  } @@ -1512,43 +1586,28 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,  }  EXPORT_SYMBOL(ocelot_port_mdb_del); -int ocelot_port_bridge_join(struct ocelot *ocelot, int port, -			    struct net_device *bridge) +void ocelot_port_bridge_join(struct ocelot *ocelot, int port, +			     struct net_device *bridge)  { -	if (!ocelot->bridge_mask) { -		ocelot->hw_bridge_dev = bridge; -	} else { -		if (ocelot->hw_bridge_dev != bridge) -			/* This is adding the port to a second bridge, this is -			 * unsupported */ -			return -ENODEV; -	} +	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	ocelot->bridge_mask |= BIT(port); +	ocelot_port->bridge = bridge; -	return 0; +	ocelot_apply_bridge_fwd_mask(ocelot);  }  EXPORT_SYMBOL(ocelot_port_bridge_join); -int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, -			     struct net_device *bridge) +void ocelot_port_bridge_leave(struct ocelot *ocelot, int port, +			      struct net_device *bridge)  { +	struct ocelot_port *ocelot_port = ocelot->ports[port];  	struct ocelot_vlan pvid = {0}, native_vlan = {0}; -	int ret; -	ocelot->bridge_mask &= ~BIT(port); - -	if (!ocelot->bridge_mask) -		ocelot->hw_bridge_dev = NULL; - -	ret = ocelot_port_vlan_filtering(ocelot, port, false); -	if (ret) -		return ret; +	ocelot_port->bridge = NULL;  	ocelot_port_set_pvid(ocelot, port, pvid);  	ocelot_port_set_native_vlan(ocelot, port, native_vlan); - -	return 0; +	ocelot_apply_bridge_fwd_mask(ocelot);  }  EXPORT_SYMBOL(ocelot_port_bridge_leave); @@ -2051,6 +2110,9 @@ int ocelot_init(struct ocelot *ocelot)  		ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);  	} + +	ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_BLACKHOLE); +  	/* Allow broadcast and unknown L2 multicast to the CPU. */  	ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),  		       ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)), diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index a41b458b1b3e..8b843d3c9189 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -220,6 +220,11 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,  						   "Last action must be GOTO");  				return -EOPNOTSUPP;  			} +			if (a->police.rate_pkt_ps) { +				NL_SET_ERR_MSG_MOD(extack, +						   "QoS offload not support packets per second"); +				return -EOPNOTSUPP; +			}  			filter->action.police_ena = true;  			rate = a->police.rate_bytes_ps;  			filter->action.pol.rate = div_u64(rate, 1000) * 8; diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 683da320bfd8..08b481a93460 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -1,9 +1,6 @@  // SPDX-License-Identifier: (GPL-2.0 OR MIT)  /* Microsemi Ocelot Switch driver   * - * This contains glue logic between the switchdev driver operations and the - * mscc_ocelot_switch_lib. - *   * Copyright (c) 2017, 2019 Microsemi Corporation   * Copyright 2020-2021 NXP Semiconductors   */ @@ -15,13 +12,34 @@  #include "ocelot.h"  #include "ocelot_vcap.h" -static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port) +static const u8 mrp_test_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x01 }; +static const u8 mrp_control_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x02 }; + +static int ocelot_mrp_find_partner_port(struct ocelot *ocelot, +					struct ocelot_port *p) +{ +	int i; + +	for (i = 0; i < ocelot->num_phys_ports; ++i) { +		struct ocelot_port *ocelot_port = ocelot->ports[i]; + +		if (!ocelot_port || p == ocelot_port) +			continue; + +		if (ocelot_port->mrp_ring_id == p->mrp_ring_id) +			return i; +	} + +	return -1; +} + +static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int id)  {  	struct ocelot_vcap_block *block_vcap_is2;  	struct ocelot_vcap_filter *filter;  	block_vcap_is2 = &ocelot->block[VCAP_IS2]; -	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port, +	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, id,  						     false);  	if (!filter)  		return 0; @@ -29,6 +47,87 @@ static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)  	return ocelot_vcap_filter_del(ocelot, filter);  } +static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, +					int dst_port) +{ +	const u8 mrp_test_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +	struct ocelot_vcap_filter *filter; +	int err; + +	filter = kzalloc(sizeof(*filter), GFP_KERNEL); +	if (!filter) +		return -ENOMEM; + +	filter->key_type = OCELOT_VCAP_KEY_ETYPE; +	filter->prio = 1; +	filter->id.cookie = src_port; +	filter->id.tc_offload = false; +	filter->block_id = VCAP_IS2; +	filter->type = OCELOT_VCAP_FILTER_OFFLOAD; +	filter->ingress_port_mask = BIT(src_port); +	ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac); +	ether_addr_copy(filter->key.etype.dmac.mask, mrp_test_mask); +	filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; +	filter->action.port_mask = BIT(dst_port); + +	err = ocelot_vcap_filter_add(ocelot, filter, NULL); +	if (err) +		kfree(filter); + +	return err; +} + +static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, +				    int prio, unsigned long cookie) +{ +	const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +	struct ocelot_vcap_filter *filter; +	int err; + +	filter = kzalloc(sizeof(*filter), GFP_KERNEL); +	if (!filter) +		return -ENOMEM; + +	filter->key_type = OCELOT_VCAP_KEY_ETYPE; +	filter->prio = prio; +	filter->id.cookie = cookie; +	filter->id.tc_offload = false; +	filter->block_id = VCAP_IS2; +	filter->type = OCELOT_VCAP_FILTER_OFFLOAD; +	filter->ingress_port_mask = BIT(port); +	/* Here is possible to use control or test dmac because the mask +	 * doesn't cover the LSB +	 */ +	ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac); +	ether_addr_copy(filter->key.etype.dmac.mask, mrp_mask); +	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; +	filter->action.port_mask = 0x0; +	filter->action.cpu_copy_ena = true; +	filter->action.cpu_qu_num = OCELOT_MRP_CPUQ; + +	err = ocelot_vcap_filter_add(ocelot, filter, NULL); +	if (err) +		kfree(filter); + +	return err; +} + +static void ocelot_mrp_save_mac(struct ocelot *ocelot, +				struct ocelot_port *port) +{ +	ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, +			  port->pvid_vlan.vid, ENTRYTYPE_LOCKED); +	ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac, +			  port->pvid_vlan.vid, ENTRYTYPE_LOCKED); +} + +static void ocelot_mrp_del_mac(struct ocelot *ocelot, +			       struct ocelot_port *port) +{ +	ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid); +	ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid); +} +  int ocelot_mrp_add(struct ocelot *ocelot, int port,  		   const struct switchdev_obj_mrp *mrp)  { @@ -45,18 +144,7 @@ int ocelot_mrp_add(struct ocelot *ocelot, int port,  	if (mrp->p_port != dev && mrp->s_port != dev)  		return 0; -	if (ocelot->mrp_ring_id != 0 && -	    ocelot->mrp_s_port && -	    ocelot->mrp_p_port) -		return -EINVAL; - -	if (mrp->p_port == dev) -		ocelot->mrp_p_port = dev; - -	if (mrp->s_port == dev) -		ocelot->mrp_s_port = dev; - -	ocelot->mrp_ring_id = mrp->ring_id; +	ocelot_port->mrp_ring_id = mrp->ring_id;  	return 0;  } @@ -66,33 +154,14 @@ int ocelot_mrp_del(struct ocelot *ocelot, int port,  		   const struct switchdev_obj_mrp *mrp)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	struct ocelot_port_private *priv; -	struct net_device *dev;  	if (!ocelot_port)  		return -EOPNOTSUPP; -	priv = container_of(ocelot_port, struct ocelot_port_private, port); -	dev = priv->dev; - -	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev) +	if (ocelot_port->mrp_ring_id != mrp->ring_id)  		return 0; -	if (ocelot->mrp_ring_id == 0 && -	    !ocelot->mrp_s_port && -	    !ocelot->mrp_p_port) -		return -EINVAL; - -	if (ocelot_mrp_del_vcap(ocelot, priv->chip_port)) -		return -EINVAL; - -	if (ocelot->mrp_p_port == dev) -		ocelot->mrp_p_port = NULL; - -	if (ocelot->mrp_s_port == dev) -		ocelot->mrp_s_port = NULL; - -	ocelot->mrp_ring_id = 0; +	ocelot_port->mrp_ring_id = 0;  	return 0;  } @@ -102,49 +171,39 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port,  			     const struct switchdev_obj_ring_role_mrp *mrp)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	struct ocelot_vcap_filter *filter; -	struct ocelot_port_private *priv; -	struct net_device *dev; +	int dst_port;  	int err;  	if (!ocelot_port)  		return -EOPNOTSUPP; -	priv = container_of(ocelot_port, struct ocelot_port_private, port); -	dev = priv->dev; - -	if (ocelot->mrp_ring_id != mrp->ring_id) -		return -EINVAL; - -	if (!mrp->sw_backup) +	if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)  		return -EOPNOTSUPP; -	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev) +	if (ocelot_port->mrp_ring_id != mrp->ring_id)  		return 0; -	filter = kzalloc(sizeof(*filter), GFP_ATOMIC); -	if (!filter) -		return -ENOMEM; +	ocelot_mrp_save_mac(ocelot, ocelot_port); -	filter->key_type = OCELOT_VCAP_KEY_ETYPE; -	filter->prio = 1; -	filter->id.cookie = priv->chip_port; -	filter->id.tc_offload = false; -	filter->block_id = VCAP_IS2; -	filter->type = OCELOT_VCAP_FILTER_OFFLOAD; -	filter->ingress_port_mask = BIT(priv->chip_port); -	*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP); -	*(__be16 *)filter->key.etype.etype.mask = htons(0xffff); -	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; -	filter->action.port_mask = 0x0; -	filter->action.cpu_copy_ena = true; -	filter->action.cpu_qu_num = OCELOT_MRP_CPUQ; +	if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) +		return ocelot_mrp_copy_add_vcap(ocelot, port, 1, port); -	err = ocelot_vcap_filter_add(ocelot, filter, NULL); +	dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port); +	if (dst_port == -1) +		return -EINVAL; + +	err = ocelot_mrp_redirect_add_vcap(ocelot, port, dst_port);  	if (err) -		kfree(filter); +		return err; -	return err; +	err = ocelot_mrp_copy_add_vcap(ocelot, port, 2, +				       port + ocelot->num_phys_ports); +	if (err) { +		ocelot_mrp_del_vcap(ocelot, port); +		return err; +	} + +	return 0;  }  EXPORT_SYMBOL(ocelot_mrp_add_ring_role); @@ -152,24 +211,32 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,  			     const struct switchdev_obj_ring_role_mrp *mrp)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	struct ocelot_port_private *priv; -	struct net_device *dev; +	int i;  	if (!ocelot_port)  		return -EOPNOTSUPP; -	priv = container_of(ocelot_port, struct ocelot_port_private, port); -	dev = priv->dev; - -	if (ocelot->mrp_ring_id != mrp->ring_id) -		return -EINVAL; - -	if (!mrp->sw_backup) +	if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)  		return -EOPNOTSUPP; -	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev) +	if (ocelot_port->mrp_ring_id != mrp->ring_id)  		return 0; -	return ocelot_mrp_del_vcap(ocelot, priv->chip_port); +	ocelot_mrp_del_vcap(ocelot, port); +	ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports); + +	for (i = 0; i < ocelot->num_phys_ports; ++i) { +		ocelot_port = ocelot->ports[i]; + +		if (!ocelot_port) +			continue; + +		if (ocelot_port->mrp_ring_id != 0) +			goto out; +	} + +	ocelot_mrp_del_mac(ocelot, ocelot->ports[port]); +out: +	return 0;  }  EXPORT_SYMBOL(ocelot_mrp_del_ring_role); diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 12cb6867a2d0..aad33d22c33f 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -251,6 +251,12 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,  			return -EEXIST;  		} +		if (action->police.rate_pkt_ps) { +			NL_SET_ERR_MSG_MOD(extack, +					   "QoS offload not support packets per second"); +			return -EOPNOTSUPP; +		} +  		pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;  		pol.burst = action->police.burst; @@ -501,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)  	/* Check if timestamping is needed */  	if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { -		rew_op = ocelot_port->ptp_cmd; +		struct sk_buff *clone = NULL; -		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { -			struct sk_buff *clone; - -			clone = skb_clone_sk(skb); -			if (!clone) { -				kfree_skb(skb); -				return NETDEV_TX_OK; -			} +		if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) { +			kfree_skb(skb); +			return NETDEV_TX_OK; +		} -			ocelot_port_add_txtstamp_skb(ocelot, port, clone); +		if (clone) +			OCELOT_SKB_CB(skb)->clone = clone; -			rew_op |= clone->cb[0] << 3; -		} +		rew_op = ocelot_ptp_rew_op(skb);  	}  	ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); @@ -1111,77 +1113,213 @@ static int ocelot_port_obj_del(struct net_device *dev,  	return ret;  } -static int ocelot_netdevice_bridge_join(struct ocelot *ocelot, int port, -					struct net_device *bridge) +static void ocelot_inherit_brport_flags(struct ocelot *ocelot, int port, +					struct net_device *brport_dev) +{ +	struct switchdev_brport_flags flags = {0}; +	int flag; + +	flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + +	for_each_set_bit(flag, &flags.mask, 32) +		if (br_port_flag_is_set(brport_dev, BIT(flag))) +			flags.val |= BIT(flag); + +	ocelot_port_bridge_flags(ocelot, port, flags); +} + +static void ocelot_clear_brport_flags(struct ocelot *ocelot, int port)  {  	struct switchdev_brport_flags flags; -	int err;  	flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; -	flags.val = flags.mask; +	flags.val = flags.mask & ~BR_LEARNING; + +	ocelot_port_bridge_flags(ocelot, port, flags); +} -	err = ocelot_port_bridge_join(ocelot, port, bridge); +static int ocelot_switchdev_sync(struct ocelot *ocelot, int port, +				 struct net_device *brport_dev, +				 struct net_device *bridge_dev, +				 struct netlink_ext_ack *extack) +{ +	clock_t ageing_time; +	u8 stp_state; +	int err; + +	ocelot_inherit_brport_flags(ocelot, port, brport_dev); + +	stp_state = br_port_get_stp_state(brport_dev); +	ocelot_bridge_stp_state_set(ocelot, port, stp_state); + +	err = ocelot_port_vlan_filtering(ocelot, port, +					 br_vlan_enabled(bridge_dev));  	if (err)  		return err; -	ocelot_port_bridge_flags(ocelot, port, flags); +	ageing_time = br_get_ageing_time(bridge_dev); +	ocelot_port_attr_ageing_set(ocelot, port, ageing_time); + +	err = br_mdb_replay(bridge_dev, brport_dev, +			    &ocelot_switchdev_blocking_nb, extack); +	if (err && err != -EOPNOTSUPP) +		return err; + +	err = br_fdb_replay(bridge_dev, brport_dev, &ocelot_switchdev_nb); +	if (err) +		return err; + +	err = br_vlan_replay(bridge_dev, brport_dev, +			     &ocelot_switchdev_blocking_nb, extack); +	if (err && err != -EOPNOTSUPP) +		return err; + +	return 0; +} + +static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port) +{ +	int err; + +	err = ocelot_port_vlan_filtering(ocelot, port, false); +	if (err) +		return err; + +	ocelot_clear_brport_flags(ocelot, port); + +	ocelot_bridge_stp_state_set(ocelot, port, BR_STATE_FORWARDING); + +	return 0; +} + +static int ocelot_netdevice_bridge_join(struct net_device *dev, +					struct net_device *brport_dev, +					struct net_device *bridge, +					struct netlink_ext_ack *extack) +{ +	struct ocelot_port_private *priv = netdev_priv(dev); +	struct ocelot_port *ocelot_port = &priv->port; +	struct ocelot *ocelot = ocelot_port->ocelot; +	int port = priv->chip_port; +	int err; + +	ocelot_port_bridge_join(ocelot, port, bridge); + +	err = ocelot_switchdev_sync(ocelot, port, brport_dev, bridge, extack); +	if (err) +		goto err_switchdev_sync;  	return 0; + +err_switchdev_sync: +	ocelot_port_bridge_leave(ocelot, port, bridge); +	return err;  } -static int ocelot_netdevice_bridge_leave(struct ocelot *ocelot, int port, +static int ocelot_netdevice_bridge_leave(struct net_device *dev, +					 struct net_device *brport_dev,  					 struct net_device *bridge)  { -	struct switchdev_brport_flags flags; +	struct ocelot_port_private *priv = netdev_priv(dev); +	struct ocelot_port *ocelot_port = &priv->port; +	struct ocelot *ocelot = ocelot_port->ocelot; +	int port = priv->chip_port;  	int err; -	flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; -	flags.val = flags.mask & ~BR_LEARNING; +	err = ocelot_switchdev_unsync(ocelot, port); +	if (err) +		return err; -	err = ocelot_port_bridge_leave(ocelot, port, bridge); +	ocelot_port_bridge_leave(ocelot, port, bridge); -	ocelot_port_bridge_flags(ocelot, port, flags); +	return 0; +} + +static int ocelot_netdevice_lag_join(struct net_device *dev, +				     struct net_device *bond, +				     struct netdev_lag_upper_info *info, +				     struct netlink_ext_ack *extack) +{ +	struct ocelot_port_private *priv = netdev_priv(dev); +	struct ocelot_port *ocelot_port = &priv->port; +	struct ocelot *ocelot = ocelot_port->ocelot; +	struct net_device *bridge_dev; +	int port = priv->chip_port; +	int err; +	err = ocelot_port_lag_join(ocelot, port, bond, info); +	if (err == -EOPNOTSUPP) { +		NL_SET_ERR_MSG_MOD(extack, "Offloading not supported"); +		return 0; +	} + +	bridge_dev = netdev_master_upper_dev_get(bond); +	if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) +		return 0; + +	err = ocelot_netdevice_bridge_join(dev, bond, bridge_dev, extack); +	if (err) +		goto err_bridge_join; + +	return 0; + +err_bridge_join: +	ocelot_port_lag_leave(ocelot, port, bond);  	return err;  } -static int ocelot_netdevice_changeupper(struct net_device *dev, -					struct netdev_notifier_changeupper_info *info) +static int ocelot_netdevice_lag_leave(struct net_device *dev, +				      struct net_device *bond)  {  	struct ocelot_port_private *priv = netdev_priv(dev);  	struct ocelot_port *ocelot_port = &priv->port;  	struct ocelot *ocelot = ocelot_port->ocelot; +	struct net_device *bridge_dev;  	int port = priv->chip_port; + +	ocelot_port_lag_leave(ocelot, port, bond); + +	bridge_dev = netdev_master_upper_dev_get(bond); +	if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) +		return 0; + +	return ocelot_netdevice_bridge_leave(dev, bond, bridge_dev); +} + +static int ocelot_netdevice_changeupper(struct net_device *dev, +					struct netdev_notifier_changeupper_info *info) +{ +	struct netlink_ext_ack *extack;  	int err = 0; +	extack = netdev_notifier_info_to_extack(&info->info); +  	if (netif_is_bridge_master(info->upper_dev)) { -		if (info->linking) { -			err = ocelot_netdevice_bridge_join(ocelot, port, -							   info->upper_dev); -		} else { -			err = ocelot_netdevice_bridge_leave(ocelot, port, +		if (info->linking) +			err = ocelot_netdevice_bridge_join(dev, dev, +							   info->upper_dev, +							   extack); +		else +			err = ocelot_netdevice_bridge_leave(dev, dev,  							    info->upper_dev); -		}  	}  	if (netif_is_lag_master(info->upper_dev)) { -		if (info->linking) { -			err = ocelot_port_lag_join(ocelot, port, -						   info->upper_dev, -						   info->upper_info); -			if (err == -EOPNOTSUPP) { -				NL_SET_ERR_MSG_MOD(info->info.extack, -						   "Offloading not supported"); -				err = 0; -			} -		} else { -			ocelot_port_lag_leave(ocelot, port, -					      info->upper_dev); -		} +		if (info->linking) +			err = ocelot_netdevice_lag_join(dev, info->upper_dev, +							info->upper_info, extack); +		else +			ocelot_netdevice_lag_leave(dev, info->upper_dev);  	}  	return notifier_from_errno(err);  } +/* Treat CHANGEUPPER events on an offloaded LAG as individual CHANGEUPPER + * events for the lower physical ports of the LAG. + * If the LAG upper isn't offloaded, ignore its CHANGEUPPER events. + * In case the LAG joined a bridge, notify that we are offloading it and can do + * forwarding in hardware towards it. + */  static int  ocelot_netdevice_lag_changeupper(struct net_device *dev,  				 struct netdev_notifier_changeupper_info *info) @@ -1191,6 +1329,12 @@ ocelot_netdevice_lag_changeupper(struct net_device *dev,  	int err = NOTIFY_DONE;  	netdev_for_each_lower_dev(dev, lower, iter) { +		struct ocelot_port_private *priv = netdev_priv(lower); +		struct ocelot_port *ocelot_port = &priv->port; + +		if (ocelot_port->bond != dev) +			return NOTIFY_OK; +  		err = ocelot_netdevice_changeupper(lower, info);  		if (err)  			return notifier_from_errno(err); diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index a33ab315cc6b..87ad2137ba06 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -4,6 +4,8 @@   * Copyright (c) 2017 Microsemi Corporation   * Copyright 2020 NXP   */ +#include <linux/time64.h> +  #include <soc/mscc/ocelot_ptp.h>  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot.h> diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c b/drivers/net/ethernet/mscc/ocelot_vcap.c index 37a232911395..7945393a0655 100644 --- a/drivers/net/ethernet/mscc/ocelot_vcap.c +++ b/drivers/net/ethernet/mscc/ocelot_vcap.c @@ -761,6 +761,7 @@ static void is1_entry_set(struct ocelot *ocelot, int ix,  			vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE,  					   etype.value, etype.mask);  		} +		break;  	}  	default:  		break;  |