Browse Source

net: mscc: ocelot: make use of SerDes PHYs for handling their configuration

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>
Quentin Schulz 6 năm trước cách đây
mục cha
commit
71e32a20cf

+ 2 - 0
drivers/net/ethernet/mscc/Kconfig

@@ -23,6 +23,8 @@ config MSCC_OCELOT_SWITCH
 config MSCC_OCELOT_SWITCH_OCELOT
 config MSCC_OCELOT_SWITCH_OCELOT
 	tristate "Ocelot switch driver on Ocelot"
 	tristate "Ocelot switch driver on Ocelot"
 	depends on MSCC_OCELOT_SWITCH
 	depends on MSCC_OCELOT_SWITCH
+	depends on GENERIC_PHY
+	depends on OF_NET
 	help
 	help
 	  This driver supports the Ocelot network switch device as present on
 	  This driver supports the Ocelot network switch device as present on
 	  the Ocelot SoCs.
 	  the Ocelot SoCs.

+ 15 - 1
drivers/net/ethernet/mscc/ocelot.c

@@ -472,6 +472,7 @@ static int ocelot_port_open(struct net_device *dev)
 {
 {
 	struct ocelot_port *port = netdev_priv(dev);
 	struct ocelot_port *port = netdev_priv(dev);
 	struct ocelot *ocelot = port->ocelot;
 	struct ocelot *ocelot = port->ocelot;
+	enum phy_mode phy_mode;
 	int err;
 	int err;
 
 
 	/* Enable receiving frames on the port, and activate auto-learning of
 	/* 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_PORTID_VAL(port->chip_port),
 			 ANA_PORT_PORT_CFG, 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,
 	err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link,
-				 PHY_INTERFACE_MODE_NA);
+				 port->phy_mode);
 	if (err) {
 	if (err) {
 		netdev_err(dev, "Could not attach to PHY\n");
 		netdev_err(dev, "Could not attach to PHY\n");
 		return err;
 		return err;

+ 5 - 1
drivers/net/ethernet/mscc/ocelot.h

@@ -11,9 +11,10 @@
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
 #include <linux/if_vlan.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regmap.h>
-#include <soc/mscc/ocelot_hsio.h>
 
 
 #include "ocelot_ana.h"
 #include "ocelot_ana.h"
 #include "ocelot_dev.h"
 #include "ocelot_dev.h"
@@ -454,6 +455,9 @@ struct ocelot_port {
 	u8 vlan_aware;
 	u8 vlan_aware;
 
 
 	u64 *stats;
 	u64 *stats;
+
+	phy_interface_t phy_mode;
+	struct phy *serdes;
 };
 };
 
 
 u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
 u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);

+ 40 - 10
drivers/net/ethernet/mscc/ocelot_board.c

@@ -6,6 +6,7 @@
  */
  */
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/of_net.h>
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/of_mdio.h>
 #include <linux/of_mdio.h>
 #include <linux/of_platform.h>
 #include <linux/of_platform.h>
@@ -253,18 +254,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 	INIT_LIST_HEAD(&ocelot->multicast);
 	INIT_LIST_HEAD(&ocelot->multicast);
 	ocelot_init(ocelot);
 	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) {
 	for_each_available_child_of_node(ports, portnp) {
 		struct device_node *phy_node;
 		struct device_node *phy_node;
 		struct phy_device *phy;
 		struct phy_device *phy;
 		struct resource *res;
 		struct resource *res;
+		struct phy *serdes;
+		enum phy_mode phy_mode;
 		void __iomem *regs;
 		void __iomem *regs;
 		char res_name[8];
 		char res_name[8];
 		u32 port;
 		u32 port;
@@ -289,10 +284,45 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 			continue;
 			continue;
 
 
 		err = ocelot_probe_port(ocelot, port, regs, phy);
 		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;
 			goto err_probe_ports;
 		}
 		}
+
+		ocelot->ports[port]->serdes = serdes;
 	}
 	}
 
 
 	register_netdevice_notifier(&ocelot_netdevice_nb);
 	register_netdevice_notifier(&ocelot_netdevice_nb);

+ 1 - 0
drivers/net/ethernet/mscc/ocelot_regs.c

@@ -5,6 +5,7 @@
  * Copyright (c) 2017 Microsemi Corporation
  * Copyright (c) 2017 Microsemi Corporation
  */
  */
 #include "ocelot.h"
 #include "ocelot.h"
+#include <soc/mscc/ocelot_hsio.h>
 
 
 static const u32 ocelot_ana_regmap[] = {
 static const u32 ocelot_ana_regmap[] = {
 	REG(ANA_ADVLEARN,                  0x009000),
 	REG(ANA_ADVLEARN,                  0x009000),