Commit 71e32a20 authored by Quentin Schulz's avatar Quentin Schulz Committed by David S. Miller
Browse files

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: default avatarQuentin Schulz <quentin.schulz@bootlin.com>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 51f6b410
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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.
+15 −1
Original line number Diff line number Diff line
@@ -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;
+5 −1
Original line number Diff line number Diff line
@@ -11,9 +11,10 @@
#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);
+40 −10
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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),