Skip to content

Commit

Permalink
net: dsa: xrs700x: add HSR offloading support
Browse files Browse the repository at this point in the history
Add offloading for HSR/PRP (IEC 62439-3) tag insertion, tag removal
forwarding and duplication supported by the xrs7000 series switches.

Only HSR v1 and PRP v1 are supported by the xrs7000 series switches (HSR
v0 is not).

Signed-off-by: George McCollister <george.mccollister@gmail.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
George McCollister authored and David S. Miller committed Feb 11, 2021
1 parent 18596f5 commit bd62e6f
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 1 deletion.
121 changes: 121 additions & 0 deletions drivers/net/dsa/xrs700x/xrs700x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/dsa/xrs700x/xrs700x_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion net/dsa/tag_xrs700x.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@

static struct sk_buff *xrs700x_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_port *partner, *dp = dsa_slave_to_port(dev);
u8 *trailer;

trailer = skb_put(skb, 1);
trailer[0] = BIT(dp->index);

if (dp->hsr_dev)
dsa_hsr_foreach_port(partner, dp->ds, dp->hsr_dev)
if (partner != dp)
trailer[0] |= BIT(partner->index);

return skb;
}

Expand Down

0 comments on commit bd62e6f

Please sign in to comment.