Commit e345e58a authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa-hsr-offload'



George McCollister says:

====================
add HSR offloading support for DSA switches

Add support for offloading HSR/PRP (IEC 62439-3) tag insertion, tag
removal, forwarding and duplication on DSA switches.
This series adds offloading to the xrs700x DSA driver.

Changes since RFC:
 * Split hsr and dsa patches. (Florian Fainelli)

Changes since v1:
 * Fixed some typos/wording. (Vladimir Oltean)
 * eliminate IFF_HSR and use is_hsr_master instead. (Vladimir Oltean)
 * Make hsr_handle_sup_frame handle skb_std as well (required when offloading)
 * Don't add hsr tag for HSR v0 supervisory frames.
 * Fixed tag insertion offloading for PRP.

Changes since v2:
 * Return -EOPNOTSUPP instead of 0 in dsa_switch_hsr_join and
   dsa_switch_hsr_leave. (Vladimir Oltean)
 * Only allow ports 1 and 2 to be HSR/PRP redundant ports. (Tobias Waldekranz)
 * Set and remove HSR features for both redundant ports. (Vladimir Oltean)
 * Change port_hsr_leave() to return int instead of void.
 * Remove hsr_init_skb() proto argument. (Vladimir Oltean)
===================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f8a7e014 bd62e6f5
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -182,3 +182,24 @@ stricter than Hardware LRO. A packet stream merged by Hardware GRO must
be re-segmentable by GSO or TSO back to the exact original packet stream.
Hardware GRO is dependent on RXCSUM since every packet successfully merged
by hardware must also have the checksum verified by hardware.

* hsr-tag-ins-offload

This should be set for devices which insert an HSR (High-availability Seamless
Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically.

* hsr-tag-rm-offload

This should be set for devices which remove HSR (High-availability Seamless
Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically.

* hsr-fwd-offload

This should be set for devices which forward HSR (High-availability Seamless
Redundancy) frames from one port to another in hardware.

* hsr-dup-offload

This should be set for devices which duplicate outgoing HSR (High-availability
Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically
frames in hardware.
+121 −0
Original line number Diff line number Diff line
@@ -7,11 +7,17 @@
#include <net/dsa.h>
#include <linux/if_bridge.h>
#include <linux/of_device.h>
#include <linux/netdev_features.h>
#include <linux/if_hsr.h>
#include "xrs700x.h"
#include "xrs700x_reg.h"

#define XRS700X_MIB_INTERVAL msecs_to_jiffies(3000)

#define XRS7000X_SUPPORTED_HSR_FEATURES \
	(NETIF_F_HW_HSR_TAG_INS | NETIF_F_HW_HSR_TAG_RM | \
	 NETIF_F_HW_HSR_FWD | NETIF_F_HW_HSR_DUP)

#define XRS7003E_ID	0x100
#define XRS7003F_ID	0x101
#define XRS7004E_ID	0x200
@@ -496,6 +502,119 @@ static void xrs700x_bridge_leave(struct dsa_switch *ds, int port,
	xrs700x_bridge_common(ds, port, bridge, false);
}

static int xrs700x_hsr_join(struct dsa_switch *ds, int port,
			    struct net_device *hsr)
{
	unsigned int val = XRS_HSR_CFG_HSR_PRP;
	struct dsa_port *partner = NULL, *dp;
	struct xrs700x *priv = ds->priv;
	struct net_device *slave;
	int ret, i, hsr_pair[2];
	enum hsr_version ver;

	ret = hsr_get_version(hsr, &ver);
	if (ret)
		return ret;

	/* Only ports 1 and 2 can be HSR/PRP redundant ports. */
	if (port != 1 && port != 2)
		return -EOPNOTSUPP;

	if (ver == HSR_V1)
		val |= XRS_HSR_CFG_HSR;
	else if (ver == PRP_V1)
		val |= XRS_HSR_CFG_PRP;
	else
		return -EOPNOTSUPP;

	dsa_hsr_foreach_port(dp, ds, hsr) {
		partner = dp;
	}

	/* We can't enable redundancy on the switch until both
	 * redundant ports have signed up.
	 */
	if (!partner)
		return 0;

	regmap_fields_write(priv->ps_forward, partner->index,
			    XRS_PORT_DISABLED);
	regmap_fields_write(priv->ps_forward, port, XRS_PORT_DISABLED);

	regmap_write(priv->regmap, XRS_HSR_CFG(partner->index),
		     val | XRS_HSR_CFG_LANID_A);
	regmap_write(priv->regmap, XRS_HSR_CFG(port),
		     val | XRS_HSR_CFG_LANID_B);

	/* Clear bits for both redundant ports (HSR only) and the CPU port to
	 * enable forwarding.
	 */
	val = GENMASK(ds->num_ports - 1, 0);
	if (ver == HSR_V1) {
		val &= ~BIT(partner->index);
		val &= ~BIT(port);
	}
	val &= ~BIT(dsa_upstream_port(ds, port));
	regmap_write(priv->regmap, XRS_PORT_FWD_MASK(partner->index), val);
	regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);

	regmap_fields_write(priv->ps_forward, partner->index,
			    XRS_PORT_FORWARDING);
	regmap_fields_write(priv->ps_forward, port, XRS_PORT_FORWARDING);

	hsr_pair[0] = port;
	hsr_pair[1] = partner->index;
	for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) {
		slave = dsa_to_port(ds, hsr_pair[i])->slave;
		slave->features |= XRS7000X_SUPPORTED_HSR_FEATURES;
	}

	return 0;
}

static int xrs700x_hsr_leave(struct dsa_switch *ds, int port,
			     struct net_device *hsr)
{
	struct dsa_port *partner = NULL, *dp;
	struct xrs700x *priv = ds->priv;
	struct net_device *slave;
	int i, hsr_pair[2];
	unsigned int val;

	dsa_hsr_foreach_port(dp, ds, hsr) {
		partner = dp;
	}

	if (!partner)
		return 0;

	regmap_fields_write(priv->ps_forward, partner->index,
			    XRS_PORT_DISABLED);
	regmap_fields_write(priv->ps_forward, port, XRS_PORT_DISABLED);

	regmap_write(priv->regmap, XRS_HSR_CFG(partner->index), 0);
	regmap_write(priv->regmap, XRS_HSR_CFG(port), 0);

	/* Clear bit for the CPU port to enable forwarding. */
	val = GENMASK(ds->num_ports - 1, 0);
	val &= ~BIT(dsa_upstream_port(ds, port));
	regmap_write(priv->regmap, XRS_PORT_FWD_MASK(partner->index), val);
	regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);

	regmap_fields_write(priv->ps_forward, partner->index,
			    XRS_PORT_FORWARDING);
	regmap_fields_write(priv->ps_forward, port, XRS_PORT_FORWARDING);

	hsr_pair[0] = port;
	hsr_pair[1] = partner->index;
	for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) {
		slave = dsa_to_port(ds, hsr_pair[i])->slave;
		slave->features &= ~XRS7000X_SUPPORTED_HSR_FEATURES;
	}

	return 0;
}

static const struct dsa_switch_ops xrs700x_ops = {
	.get_tag_protocol	= xrs700x_get_tag_protocol,
	.setup			= xrs700x_setup,
@@ -509,6 +628,8 @@ static const struct dsa_switch_ops xrs700x_ops = {
	.get_stats64		= xrs700x_get_stats64,
	.port_bridge_join	= xrs700x_bridge_join,
	.port_bridge_leave	= xrs700x_bridge_leave,
	.port_hsr_join		= xrs700x_hsr_join,
	.port_hsr_leave		= xrs700x_hsr_leave,
};

static int xrs700x_detect(struct xrs700x *priv)
+5 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@

/* Port Configuration Registers - HSR/PRP */
#define XRS_HSR_CFG(x)			(XRS_PORT_HSR_BASE(x) + 0x0)
#define XRS_HSR_CFG_HSR_PRP		BIT(0)
#define XRS_HSR_CFG_HSR			0
#define XRS_HSR_CFG_PRP			BIT(8)
#define XRS_HSR_CFG_LANID_A		0
#define XRS_HSR_CFG_LANID_B		BIT(10)

/* Port Configuration Registers - PTP */
#define XRS_PTP_RX_SYNC_DELAY_NS_LO(x)	(XRS_PORT_PTP_BASE(x) + 0x2)

include/linux/if_hsr.h

0 → 100644
+27 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_IF_HSR_H_
#define _LINUX_IF_HSR_H_

/* used to differentiate various protocols */
enum hsr_version {
	HSR_V0 = 0,
	HSR_V1,
	PRP_V1,
};

#if IS_ENABLED(CONFIG_HSR)
extern bool is_hsr_master(struct net_device *dev);
extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
#else
static inline bool is_hsr_master(struct net_device *dev)
{
	return false;
}
static inline int hsr_get_version(struct net_device *dev,
				  enum hsr_version *ver)
{
	return -EINVAL;
}
#endif /* CONFIG_HSR */

#endif /*_LINUX_IF_HSR_H_*/
+9 −0
Original line number Diff line number Diff line
@@ -86,6 +86,11 @@ enum {
	NETIF_F_HW_MACSEC_BIT,		/* Offload MACsec operations */
	NETIF_F_GRO_UDP_FWD_BIT,	/* Allow UDP GRO for forwarding */

	NETIF_F_HW_HSR_TAG_INS_BIT,	/* Offload HSR tag insertion */
	NETIF_F_HW_HSR_TAG_RM_BIT,	/* Offload HSR tag removal */
	NETIF_F_HW_HSR_FWD_BIT,		/* Offload HSR forwarding */
	NETIF_F_HW_HSR_DUP_BIT,		/* Offload HSR duplication */

	/*
	 * Add your fresh new feature above and remember to update
	 * netdev_features_strings[] in net/core/ethtool.c and maybe
@@ -159,6 +164,10 @@ enum {
#define NETIF_F_GSO_FRAGLIST	__NETIF_F(GSO_FRAGLIST)
#define NETIF_F_HW_MACSEC	__NETIF_F(HW_MACSEC)
#define NETIF_F_GRO_UDP_FWD	__NETIF_F(GRO_UDP_FWD)
#define NETIF_F_HW_HSR_TAG_INS	__NETIF_F(HW_HSR_TAG_INS)
#define NETIF_F_HW_HSR_TAG_RM	__NETIF_F(HW_HSR_TAG_RM)
#define NETIF_F_HW_HSR_FWD	__NETIF_F(HW_HSR_FWD)
#define NETIF_F_HW_HSR_DUP	__NETIF_F(HW_HSR_DUP)

/* Finds the next feature with the highest number of the range of start till 0.
 */
Loading