All devices are capable of using regular DSA tags. Support for
Ethertyped DSA tags sort into three categories:

1. No support. Older chips fall into this category.

2. Full support. Datasheet explicitly supports configuring the CPU
   port to receive FORWARDs with a DSA tag.

3. Undocumented support. Datasheet lists the configuration from
   category 2 as "reserved for future use", but does empirically
   behave like a category 2 device.

Because there are ethernet controllers that do not handle regular DSA
tags in all cases, it is sometimes preferable to rely on the
undocumented behavior, as the alternative is a very crippled
system. But, in those cases, make sure to log the fact that an
undocumented feature has been enabled.

Signed-off-by: Tobias Waldekranz <tob...@waldekranz.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c | 41 +++++++++++++++++++++++++++++---
 drivers/net/dsa/mv88e6xxx/chip.h |  3 +++
 2 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 95f07fcd4f85..e7ec883d5f6b 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2531,10 +2531,10 @@ static int mv88e6xxx_setup_port_mode(struct 
mv88e6xxx_chip *chip, int port)
                return mv88e6xxx_set_port_mode_normal(chip, port);
 
        /* Setup CPU port mode depending on its supported tag format */
-       if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
+       if (chip->tag_protocol == DSA_TAG_PROTO_DSA)
                return mv88e6xxx_set_port_mode_dsa(chip, port);
 
-       if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
+       if (chip->tag_protocol == DSA_TAG_PROTO_EDSA)
                return mv88e6xxx_set_port_mode_edsa(chip, port);
 
        return -EINVAL;
@@ -5564,7 +5564,39 @@ static enum dsa_tag_protocol 
mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
 {
        struct mv88e6xxx_chip *chip = ds->priv;
 
-       return chip->info->tag_protocol;
+       return chip->tag_protocol;
+}
+
+static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port,
+                                        enum dsa_tag_protocol proto)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       enum dsa_tag_protocol old_protocol;
+       int err;
+
+       switch (proto) {
+       case DSA_TAG_PROTO_EDSA:
+               if (chip->info->tag_protocol != DSA_TAG_PROTO_EDSA)
+                       dev_warn(chip->dev, "Relying on undocumented EDSA 
tagging behavior\n");
+
+               break;
+       case DSA_TAG_PROTO_DSA:
+               break;
+       default:
+               return -EPROTONOSUPPORT;
+       }
+
+       old_protocol = chip->tag_protocol;
+       chip->tag_protocol = proto;
+
+       mv88e6xxx_reg_lock(chip);
+       err = mv88e6xxx_setup_port_mode(chip, port);
+       mv88e6xxx_reg_unlock(chip);
+
+       if (err)
+               chip->tag_protocol = old_protocol;
+
+       return err;
 }
 
 static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
@@ -6029,6 +6061,7 @@ static int mv88e6xxx_crosschip_lag_leave(struct 
dsa_switch *ds, int sw_index,
 
 static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .get_tag_protocol       = mv88e6xxx_get_tag_protocol,
+       .change_tag_protocol    = mv88e6xxx_change_tag_protocol,
        .setup                  = mv88e6xxx_setup,
        .teardown               = mv88e6xxx_teardown,
        .phylink_validate       = mv88e6xxx_validate,
@@ -6209,6 +6242,8 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
        if (err)
                goto out;
 
+       chip->tag_protocol = chip->info->tag_protocol;
+
        mv88e6xxx_phy_init(chip);
 
        if (chip->info->ops->get_eeprom) {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index bce6e0dc8535..96b775f3fda2 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -261,6 +261,9 @@ struct mv88e6xxx_region_priv {
 struct mv88e6xxx_chip {
        const struct mv88e6xxx_info *info;
 
+       /* Currently configured tagging protocol */
+       enum dsa_tag_protocol tag_protocol;
+
        /* The dsa_switch this private structure is related to */
        struct dsa_switch *ds;
 
-- 
2.25.1

Reply via email to