dsa: mv88e6xxx: Allow speed/duplex of port to be configured
authorAndrew Lunn <andrew@lunn.ch>
Mon, 31 Aug 2015 13:56:47 +0000 (15:56 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 31 Aug 2015 21:48:01 +0000 (14:48 -0700)
The current code sets user ports to perform auto negotiation using the
phy. CPU and DSA ports are configured to full duplex and maximum speed
the switch supports.

There are however use cases where the CPU has a slower port, and when
user ports have SFP modules with fixed speed. In these cases, port
settings to be read from a fixed_phy devices. The switch driver then
needs to implement the adjust_link op, so the port settings can be
set.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6123_61_65.c
drivers/net/dsa/mv88e6131.c
drivers/net/dsa/mv88e6171.c
drivers/net/dsa/mv88e6352.c
drivers/net/dsa/mv88e6xxx.c
drivers/net/dsa/mv88e6xxx.h

index 71a29a7ce538d70193c2813bfd10dc4947237f24..3de2a6d73fdc4dff0ac2f7071b95bbeb4bf33015 100644 (file)
@@ -129,6 +129,7 @@ struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
+       .adjust_link            = mv88e6xxx_adjust_link,
 #ifdef CONFIG_NET_DSA_HWMON
        .get_temp               = mv88e6xxx_get_temp,
 #endif
index 32f4a08e9bc99dce2fa59af8cae8b34475bb24d6..3e838652996507d673b918e52ec3953d07795d3f 100644 (file)
@@ -182,6 +182,7 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
+       .adjust_link            = mv88e6xxx_adjust_link,
 };
 
 MODULE_ALIAS("platform:mv88e6085");
index 735f04cd83eec7e1b629695133c5806bcb3fff08..d54b7400e8d820b334a45afab9003e2150732242 100644 (file)
@@ -108,6 +108,7 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
+       .adjust_link            = mv88e6xxx_adjust_link,
 #ifdef CONFIG_NET_DSA_HWMON
        .get_temp               = mv88e6xxx_get_temp,
 #endif
index 14b71779df99d76988127ae77958d99796396a55..1f5129c105fb3cce995527104b049ebf6e89abac 100644 (file)
@@ -328,6 +328,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
        .get_strings            = mv88e6xxx_get_strings,
        .get_ethtool_stats      = mv88e6xxx_get_ethtool_stats,
        .get_sset_count         = mv88e6xxx_get_sset_count,
+       .adjust_link            = mv88e6xxx_adjust_link,
        .set_eee                = mv88e6xxx_set_eee,
        .get_eee                = mv88e6xxx_get_eee,
 #ifdef CONFIG_NET_DSA_HWMON
index 3774f53d28d781aaec7f5150352a944ba505d974..1a8c45f3e68057d1b9c8f8044c618bc9b68f7425 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/if_bridge.h>
 #include <linux/jiffies.h>
 #include <linux/list.h>
@@ -560,6 +561,63 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
        return false;
 }
 
+/* We expect the switch to perform auto negotiation if there is a real
+ * phy. However, in the case of a fixed link phy, we force the port
+ * settings from the fixed link settings.
+ */
+void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
+                          struct phy_device *phydev)
+{
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+       u32 ret, reg;
+
+       if (!phy_is_pseudo_fixed_link(phydev))
+               return;
+
+       mutex_lock(&ps->smi_mutex);
+
+       ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
+       if (ret < 0)
+               goto out;
+
+       reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
+                     PORT_PCS_CTRL_FORCE_LINK |
+                     PORT_PCS_CTRL_DUPLEX_FULL |
+                     PORT_PCS_CTRL_FORCE_DUPLEX |
+                     PORT_PCS_CTRL_UNFORCED);
+
+       reg |= PORT_PCS_CTRL_FORCE_LINK;
+       if (phydev->link)
+                       reg |= PORT_PCS_CTRL_LINK_UP;
+
+       if (mv88e6xxx_6065_family(ds) && phydev->speed > SPEED_100)
+               goto out;
+
+       switch (phydev->speed) {
+       case SPEED_1000:
+               reg |= PORT_PCS_CTRL_1000;
+               break;
+       case SPEED_100:
+               reg |= PORT_PCS_CTRL_100;
+               break;
+       case SPEED_10:
+               reg |= PORT_PCS_CTRL_10;
+               break;
+       default:
+               pr_info("Unknown speed");
+               goto out;
+       }
+
+       reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
+       if (phydev->duplex == DUPLEX_FULL)
+               reg |= PORT_PCS_CTRL_DUPLEX_FULL;
+
+       _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_PCS_CTRL, reg);
+
+out:
+       mutex_unlock(&ps->smi_mutex);
+}
+
 /* Must be called with SMI mutex held */
 static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
 {
index 72ca887feb0d56bafb47334b28eafe660efc418d..79003c55fe62c96c09a224f86d0258e6e1c43d44 100644 (file)
@@ -446,6 +446,8 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
                                 uint64_t *data);
 int mv88e6xxx_get_sset_count(struct dsa_switch *ds);
 int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds);
+void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
+                          struct phy_device *phydev);
 int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port);
 void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
                        struct ethtool_regs *regs, void *_p);