From 955222ca5281ee7ded6b899605c055b147a15c73 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:48 -0400 Subject: [PATCH 1/6] net: dsa: use a single switch statement for port setup It is currently difficult to read the different steps involved in the setup and teardown of ports in the DSA code. Keep it simple with a single switch statement for each port type: UNUSED, CPU, DSA, or USER. Also no need to call devlink_port_unregister from within dsa_port_setup as this step is inconditionally handled by dsa_port_teardown on error. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 87 ++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 3abd173ebacb..405552ac4c08 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -254,88 +254,79 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) static int dsa_port_setup(struct dsa_port *dp) { - enum devlink_port_flavour flavour; struct dsa_switch *ds = dp->ds; struct dsa_switch_tree *dst = ds->dst; - int err = 0; - - if (dp->type == DSA_PORT_TYPE_UNUSED) - return 0; - - memset(&dp->devlink_port, 0, sizeof(dp->devlink_port)); - dp->mac = of_get_mac_address(dp->dn); - - switch (dp->type) { - case DSA_PORT_TYPE_CPU: - flavour = DEVLINK_PORT_FLAVOUR_CPU; - break; - case DSA_PORT_TYPE_DSA: - flavour = DEVLINK_PORT_FLAVOUR_DSA; - break; - case DSA_PORT_TYPE_USER: /* fall-through */ - default: - flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; - break; - } - - /* dp->index is used now as port_number. However - * CPU and DSA ports should have separate numbering - * independent from front panel port numbers. - */ - devlink_port_attrs_set(&dp->devlink_port, flavour, - dp->index, false, 0, - (const char *) &dst->index, sizeof(dst->index)); - err = devlink_port_register(ds->devlink, &dp->devlink_port, - dp->index); - if (err) - return err; + const unsigned char *id = (const unsigned char *)&dst->index; + const unsigned char len = sizeof(dst->index); + struct devlink_port *dlp = &dp->devlink_port; + struct devlink *dl = ds->devlink; + int err; switch (dp->type) { case DSA_PORT_TYPE_UNUSED: break; case DSA_PORT_TYPE_CPU: + memset(dlp, 0, sizeof(*dlp)); + devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_CPU, + dp->index, false, 0, id, len); + err = devlink_port_register(dl, dlp, dp->index); + if (err) + return err; + err = dsa_port_link_register_of(dp); if (err) - dev_err(ds->dev, "failed to setup link for port %d.%d\n", - ds->index, dp->index); + return err; break; case DSA_PORT_TYPE_DSA: + memset(dlp, 0, sizeof(*dlp)); + devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_DSA, + dp->index, false, 0, id, len); + err = devlink_port_register(dl, dlp, dp->index); + if (err) + return err; + err = dsa_port_link_register_of(dp); if (err) - dev_err(ds->dev, "failed to setup link for port %d.%d\n", - ds->index, dp->index); + return err; break; case DSA_PORT_TYPE_USER: + memset(dlp, 0, sizeof(*dlp)); + devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_PHYSICAL, + dp->index, false, 0, id, len); + err = devlink_port_register(dl, dlp, dp->index); + if (err) + return err; + + dp->mac = of_get_mac_address(dp->dn); err = dsa_slave_create(dp); if (err) - dev_err(ds->dev, "failed to create slave for port %d.%d\n", - ds->index, dp->index); - else - devlink_port_type_eth_set(&dp->devlink_port, dp->slave); + return err; + + devlink_port_type_eth_set(dlp, dp->slave); break; } - if (err) - devlink_port_unregister(&dp->devlink_port); - - return err; + return 0; } static void dsa_port_teardown(struct dsa_port *dp) { - if (dp->type != DSA_PORT_TYPE_UNUSED) - devlink_port_unregister(&dp->devlink_port); + struct devlink_port *dlp = &dp->devlink_port; switch (dp->type) { case DSA_PORT_TYPE_UNUSED: break; case DSA_PORT_TYPE_CPU: dsa_tag_driver_put(dp->tag_ops); - /* fall-through */ + devlink_port_unregister(dlp); + dsa_port_link_unregister_of(dp); + break; case DSA_PORT_TYPE_DSA: + devlink_port_unregister(dlp); dsa_port_link_unregister_of(dp); break; case DSA_PORT_TYPE_USER: + devlink_port_unregister(dlp); if (dp->slave) { dsa_slave_destroy(dp->slave); dp->slave = NULL; From 74be4babe72fd1ed1bba6b52d0bdc0d1e13f7af8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:49 -0400 Subject: [PATCH 2/6] net: dsa: do not enable or disable non user ports The .port_enable and .port_disable operations are currently only called for user ports, hence assuming they have a slave device. In preparation for using these operations for other port types as well, simply guard all implementations against non user ports and return directly in such case. Note that bcm_sf2_sw_suspend() currently calls bcm_sf2_port_disable() (and thus b53_disable_port()) against the user and CPU ports, so do not guards those functions. They will be called for unused ports in the future, but that was expected by those drivers anyway. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 7 ++++++- drivers/net/dsa/bcm_sf2.c | 3 +++ drivers/net/dsa/lan9303-core.c | 6 ++++++ drivers/net/dsa/lantiq_gswip.c | 6 ++++++ drivers/net/dsa/microchip/ksz_common.c | 6 ++++++ drivers/net/dsa/mt7530.c | 6 ++++++ drivers/net/dsa/mv88e6xxx/chip.c | 6 ++++++ 7 files changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 907af62846ba..7d328a5f0161 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -510,10 +510,15 @@ EXPORT_SYMBOL(b53_imp_vlan_setup); int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct b53_device *dev = ds->priv; - unsigned int cpu_port = ds->ports[port].cpu_dp->index; + unsigned int cpu_port; int ret = 0; u16 pvlan; + if (!dsa_is_user_port(ds, port)) + return 0; + + cpu_port = ds->ports[port].cpu_dp->index; + if (dev->ops->irq_enable) ret = dev->ops->irq_enable(dev, port); if (ret) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 49f99436018a..4f839348011d 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -157,6 +157,9 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, unsigned int i; u32 reg; + if (!dsa_is_user_port(ds, port)) + return 0; + /* Clear the memory power down */ reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL); reg &= ~P_TXQ_PSM_VDD(port); diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 7a2063e7737a..bbec86b9418e 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1079,6 +1079,9 @@ static int lan9303_port_enable(struct dsa_switch *ds, int port, { struct lan9303 *chip = ds->priv; + if (!dsa_is_user_port(ds, port)) + return 0; + return lan9303_enable_processing_port(chip, port); } @@ -1086,6 +1089,9 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port) { struct lan9303 *chip = ds->priv; + if (!dsa_is_user_port(ds, port)) + return; + lan9303_disable_processing_port(chip, port); lan9303_phy_write(ds, chip->phy_addr_base + port, MII_BMCR, BMCR_PDOWN); } diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 2175ec13bb2c..a69c9b9878b7 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -642,6 +642,9 @@ static int gswip_port_enable(struct dsa_switch *ds, int port, struct gswip_priv *priv = ds->priv; int err; + if (!dsa_is_user_port(ds, port)) + return 0; + if (!dsa_is_cpu_port(ds, port)) { err = gswip_add_single_port_br(priv, port, true); if (err) @@ -678,6 +681,9 @@ static void gswip_port_disable(struct dsa_switch *ds, int port) { struct gswip_priv *priv = ds->priv; + if (!dsa_is_user_port(ds, port)) + return; + if (!dsa_is_cpu_port(ds, port)) { gswip_mdio_mask(priv, GSWIP_MDIO_PHY_LINK_DOWN, GSWIP_MDIO_PHY_LINK_MASK, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b45c7b972cec..b0b870f0c252 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -361,6 +361,9 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct ksz_device *dev = ds->priv; + if (!dsa_is_user_port(ds, port)) + return 0; + /* setup slave port */ dev->dev_ops->port_setup(dev, port, false); if (dev->dev_ops->phy_setup) @@ -378,6 +381,9 @@ void ksz_disable_port(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; + if (!dsa_is_user_port(ds, port)) + return; + dev->on_ports &= ~(1 << port); dev->live_ports &= ~(1 << port); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 3181e95586d6..c48e29486b10 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -726,6 +726,9 @@ mt7530_port_enable(struct dsa_switch *ds, int port, { struct mt7530_priv *priv = ds->priv; + if (!dsa_is_user_port(ds, port)) + return 0; + mutex_lock(&priv->reg_mutex); /* Setup the MAC for the user port */ @@ -751,6 +754,9 @@ mt7530_port_disable(struct dsa_switch *ds, int port) { struct mt7530_priv *priv = ds->priv; + if (!dsa_is_user_port(ds, port)) + return; + mutex_lock(&priv->reg_mutex); /* Clear up all port matrix which could be restored in the next diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 9b3ad22a5b98..5e557545df6d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2267,6 +2267,9 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; + if (!dsa_is_user_port(ds, port)) + return 0; + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_serdes_power(chip, port, true); @@ -2283,6 +2286,9 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; + if (!dsa_is_user_port(ds, port)) + return; + mv88e6xxx_reg_lock(chip); if (mv88e6xxx_port_set_state(chip, port, BR_STATE_DISABLED)) From 0394a63acfe2a6e1c08af0eb1a9133ee8650d7bd Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:50 -0400 Subject: [PATCH 3/6] net: dsa: enable and disable all ports Call the .port_enable and .port_disable functions for all ports, not only the user ports, so that drivers may optimize the power consumption of all ports after a successful setup. Unused ports are now disabled on setup. CPU and DSA ports are now enabled on setup and disabled on teardown. User ports were already enabled at slave creation and disabled at slave destruction. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 405552ac4c08..8c4eccb0cfe6 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -264,6 +264,7 @@ static int dsa_port_setup(struct dsa_port *dp) switch (dp->type) { case DSA_PORT_TYPE_UNUSED: + dsa_port_disable(dp); break; case DSA_PORT_TYPE_CPU: memset(dlp, 0, sizeof(*dlp)); @@ -274,6 +275,10 @@ static int dsa_port_setup(struct dsa_port *dp) return err; err = dsa_port_link_register_of(dp); + if (err) + return err; + + err = dsa_port_enable(dp, NULL); if (err) return err; break; @@ -286,6 +291,10 @@ static int dsa_port_setup(struct dsa_port *dp) return err; err = dsa_port_link_register_of(dp); + if (err) + return err; + + err = dsa_port_enable(dp, NULL); if (err) return err; break; @@ -317,11 +326,13 @@ static void dsa_port_teardown(struct dsa_port *dp) case DSA_PORT_TYPE_UNUSED: break; case DSA_PORT_TYPE_CPU: + dsa_port_disable(dp); dsa_tag_driver_put(dp->tag_ops); devlink_port_unregister(dlp); dsa_port_link_unregister_of(dp); break; case DSA_PORT_TYPE_DSA: + dsa_port_disable(dp); devlink_port_unregister(dlp); dsa_port_link_unregister_of(dp); break; From 3903f315165dff72796e46d177aaf1cbd67aa07d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:51 -0400 Subject: [PATCH 4/6] net: dsa: mv88e6xxx: do not change STP state on port disabling When disabling a port, that is not for the driver to decide what to do with the STP state. This is already handled by the DSA layer. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5e557545df6d..27e1622bb03b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2291,9 +2291,6 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) mv88e6xxx_reg_lock(chip); - if (mv88e6xxx_port_set_state(chip, port, BR_STATE_DISABLED)) - dev_err(chip->dev, "failed to disable port\n"); - if (chip->info->ops->serdes_irq_free) chip->info->ops->serdes_irq_free(chip, port); From b759f528ca3dea889ab985265be46715c3586eef Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:52 -0400 Subject: [PATCH 5/6] net: dsa: mv88e6xxx: enable SERDES after setup SERDES is powered on for CPU and DSA ports and powered down for unused ports at setup time. But now that DSA calls mv88e6xxx_port_enable and mv88e6xxx_port_disable for all ports, the SERDES power can now be handled after setup inconditionally for all ports. Using the port enable and disable callbacks also have the benefit to handle the SERDES IRQ for non user ports as well. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 35 ++++---------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 27e1622bb03b..c72b3db75c54 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2151,16 +2151,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; - /* Enable the SERDES interface for DSA and CPU ports. Normal - * ports SERDES are enabled when the port is enabled, thus - * saving a bit of power. - */ - if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) { - err = mv88e6xxx_serdes_power(chip, port, true); - if (err) - return err; - } - /* Port Control 2: don't force a good FCS, set the maximum frame size to * 10240 bytes, disable 802.1q tags checking, don't discard tagged or * untagged frames on this port, do a destination address lookup on all @@ -2267,9 +2257,6 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; - if (!dsa_is_user_port(ds, port)) - return 0; - mv88e6xxx_reg_lock(chip); err = mv88e6xxx_serdes_power(chip, port, true); @@ -2286,9 +2273,6 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; - if (!dsa_is_user_port(ds, port)) - return; - mv88e6xxx_reg_lock(chip); if (chip->info->ops->serdes_irq_free) @@ -2461,27 +2445,16 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (dsa_is_unused_port(ds, i)) + continue; + /* Prevent the use of an invalid port. */ - if (mv88e6xxx_is_invalid_port(chip, i) && - !dsa_is_unused_port(ds, i)) { + if (mv88e6xxx_is_invalid_port(chip, i)) { dev_err(chip->dev, "port %d is invalid\n", i); err = -EINVAL; goto unlock; } - if (dsa_is_unused_port(ds, i)) { - err = mv88e6xxx_port_set_state(chip, i, - BR_STATE_DISABLED); - if (err) - goto unlock; - - err = mv88e6xxx_serdes_power(chip, i, false); - if (err) - goto unlock; - - continue; - } - err = mv88e6xxx_setup_port(chip, i); if (err) goto unlock; From fc0bc0190bc5ea1e44317c84c9f92f9196a7441b Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 19 Aug 2019 16:00:53 -0400 Subject: [PATCH 6/6] net: dsa: mv88e6xxx: wrap SERDES IRQ in power function Now that mv88e6xxx_serdes_power is only called after driver setup, we can wrap the SERDES IRQ code directly within it for clarity. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c72b3db75c54..d0bf98c10b2b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2057,10 +2057,26 @@ static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { - if (chip->info->ops->serdes_power) - return chip->info->ops->serdes_power(chip, port, on); + int err; - return 0; + if (!chip->info->ops->serdes_power) + return 0; + + if (on) { + err = chip->info->ops->serdes_power(chip, port, true); + if (err) + return err; + + if (chip->info->ops->serdes_irq_setup) + err = chip->info->ops->serdes_irq_setup(chip, port); + } else { + if (chip->info->ops->serdes_irq_free) + chip->info->ops->serdes_irq_free(chip, port); + + err = chip->info->ops->serdes_power(chip, port, false); + } + + return err; } static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) @@ -2258,12 +2274,7 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_serdes_power(chip, port, true); - - if (!err && chip->info->ops->serdes_irq_setup) - err = chip->info->ops->serdes_irq_setup(chip, port); - mv88e6xxx_reg_unlock(chip); return err; @@ -2274,13 +2285,8 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) struct mv88e6xxx_chip *chip = ds->priv; mv88e6xxx_reg_lock(chip); - - if (chip->info->ops->serdes_irq_free) - chip->info->ops->serdes_irq_free(chip, port); - if (mv88e6xxx_serdes_power(chip, port, false)) dev_err(chip->dev, "failed to power off SERDES\n"); - mv88e6xxx_reg_unlock(chip); }