net: mscc: ocelot: make use of SerDes PHYs for handling their configuration
authorQuentin Schulz <quentin.schulz@bootlin.com>
Thu, 4 Oct 2018 12:22:08 +0000 (14:22 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 5 Oct 2018 21:36:44 +0000 (14:36 -0700)
Previously, the SerDes muxing was hardcoded to a given mode in the MAC
controller driver. Now, the SerDes muxing is configured within the
Device Tree and is enforced in the MAC controller driver so we can have
a lot of different SerDes configurations.

Make use of the SerDes PHYs in the MAC controller to set up the SerDes
according to the SerDes<->switch port mapping and the communication mode
with the Ethernet PHY.

Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mscc/Kconfig
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_board.c
drivers/net/ethernet/mscc/ocelot_regs.c

index 36c8462..bcec058 100644 (file)
@@ -23,6 +23,8 @@ config MSCC_OCELOT_SWITCH
 config MSCC_OCELOT_SWITCH_OCELOT
        tristate "Ocelot switch driver on Ocelot"
        depends on MSCC_OCELOT_SWITCH
+       depends on GENERIC_PHY
+       depends on OF_NET
        help
          This driver supports the Ocelot network switch device as present on
          the Ocelot SoCs.
index 1a4f2bb..8f11fdb 100644 (file)
@@ -472,6 +472,7 @@ static int ocelot_port_open(struct net_device *dev)
 {
        struct ocelot_port *port = netdev_priv(dev);
        struct ocelot *ocelot = port->ocelot;
+       enum phy_mode phy_mode;
        int err;
 
        /* Enable receiving frames on the port, and activate auto-learning of
@@ -482,8 +483,21 @@ static int ocelot_port_open(struct net_device *dev)
                         ANA_PORT_PORT_CFG_PORTID_VAL(port->chip_port),
                         ANA_PORT_PORT_CFG, port->chip_port);
 
+       if (port->serdes) {
+               if (port->phy_mode == PHY_INTERFACE_MODE_SGMII)
+                       phy_mode = PHY_MODE_SGMII;
+               else
+                       phy_mode = PHY_MODE_QSGMII;
+
+               err = phy_set_mode(port->serdes, phy_mode);
+               if (err) {
+                       netdev_err(dev, "Could not set mode of SerDes\n");
+                       return err;
+               }
+       }
+
        err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link,
-                                PHY_INTERFACE_MODE_NA);
+                                port->phy_mode);
        if (err) {
                netdev_err(dev, "Could not attach to PHY\n");
                return err;
index ff0e3a5..62c7c8e 100644 (file)
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <soc/mscc/ocelot_hsio.h>
 
 #include "ocelot_ana.h"
 #include "ocelot_dev.h"
@@ -454,6 +455,9 @@ struct ocelot_port {
        u8 vlan_aware;
 
        u64 *stats;
+
+       phy_interface_t phy_mode;
+       struct phy *serdes;
 };
 
 u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
index dca205e..953b326 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/of_net.h>
 #include <linux/netdevice.h>
 #include <linux/of_mdio.h>
 #include <linux/of_platform.h>
@@ -253,18 +254,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&ocelot->multicast);
        ocelot_init(ocelot);
 
-       ocelot_rmw(ocelot, HSIO_HW_CFG_DEV1G_4_MODE |
-                    HSIO_HW_CFG_DEV1G_6_MODE |
-                    HSIO_HW_CFG_DEV1G_9_MODE,
-                    HSIO_HW_CFG_DEV1G_4_MODE |
-                    HSIO_HW_CFG_DEV1G_6_MODE |
-                    HSIO_HW_CFG_DEV1G_9_MODE,
-                    HSIO_HW_CFG);
-
        for_each_available_child_of_node(ports, portnp) {
                struct device_node *phy_node;
                struct phy_device *phy;
                struct resource *res;
+               struct phy *serdes;
+               enum phy_mode phy_mode;
                void __iomem *regs;
                char res_name[8];
                u32 port;
@@ -289,10 +284,45 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
                        continue;
 
                err = ocelot_probe_port(ocelot, port, regs, phy);
-               if (err) {
-                       dev_err(&pdev->dev, "failed to probe ports\n");
+               if (err)
+                       return err;
+
+               err = of_get_phy_mode(portnp);
+               if (err < 0)
+                       ocelot->ports[port]->phy_mode = PHY_INTERFACE_MODE_NA;
+               else
+                       ocelot->ports[port]->phy_mode = err;
+
+               switch (ocelot->ports[port]->phy_mode) {
+               case PHY_INTERFACE_MODE_NA:
+                       continue;
+               case PHY_INTERFACE_MODE_SGMII:
+                       phy_mode = PHY_MODE_SGMII;
+                       break;
+               case PHY_INTERFACE_MODE_QSGMII:
+                       phy_mode = PHY_MODE_QSGMII;
+                       break;
+               default:
+                       dev_err(ocelot->dev,
+                               "invalid phy mode for port%d, (Q)SGMII only\n",
+                               port);
+                       return -EINVAL;
+               }
+
+               serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
+               if (IS_ERR(serdes)) {
+                       err = PTR_ERR(serdes);
+                       if (err == -EPROBE_DEFER)
+                               dev_dbg(ocelot->dev, "deferring probe\n");
+                       else
+                               dev_err(ocelot->dev,
+                                       "missing SerDes phys for port%d\n",
+                                       port);
+
                        goto err_probe_ports;
                }
+
+               ocelot->ports[port]->serdes = serdes;
        }
 
        register_netdevice_notifier(&ocelot_netdevice_nb);
index 2518ce0..9271af1 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (c) 2017 Microsemi Corporation
  */
 #include "ocelot.h"
+#include <soc/mscc/ocelot_hsio.h>
 
 static const u32 ocelot_ana_regmap[] = {
        REG(ANA_ADVLEARN,                  0x009000),