Commit 71947923 authored by Ioana Ciornei's avatar Ioana Ciornei Committed by David S. Miller
Browse files

dpaa2-eth: add MAC/PHY support through phylink



The dpaa2-eth driver now has support for connecting to its associated
PHY device found through standard OF bindings.

This happens when the DPNI object (that the driver probes on) gets
connected to a DPMAC. When that happens, the device tree is looked up by
the DPMAC ID, and the associated PHY bindings are found.

The old logic of handling the net device's link state by hand still
needs to be kept, as the DPNI can be connected to other devices on the
bus than a DPMAC: other DPNI, DPSW ports, etc. This logic is only
engaged when there is no DPMAC (and therefore no phylink instance)
attached.

The MC firmware support multiple type of DPMAC links: TYPE_FIXED,
TYPE_PHY. The TYPE_FIXED mode does not require any DPMAC management from
Linux side, and as such, the driver will not handle such a DPMAC.

Although PHYLINK typically handles SFP cages and in-band AN modes, for
the moment the driver only supports the RGMII interfaces found on the
LX2160A. Support for other modes will come later.

Signed-off-by: default avatarIoana Ciornei <ioana.ciornei@nxp.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f5c3fffa
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -5053,7 +5053,9 @@ M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
L:	netdev@vger.kernel.org
S:	Maintained
F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-eth*
F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
F:	drivers/net/ethernet/freescale/dpaa2/dpni*
F:	drivers/net/ethernet/freescale/dpaa2/dpmac*
F:	drivers/net/ethernet/freescale/dpaa2/dpkg.h
F:	drivers/net/ethernet/freescale/dpaa2/Makefile
F:	drivers/net/ethernet/freescale/dpaa2/Kconfig
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
obj-$(CONFIG_FSL_DPAA2_ETH)		+= fsl-dpaa2-eth.o
obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)	+= fsl-dpaa2-ptp.o

fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o
fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o
fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
fsl-dpaa2-ptp-objs	:= dpaa2-ptp.o dprtc.o

+99 −20
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2014-2016 Freescale Semiconductor Inc.
 * Copyright 2016-2017 NXP
 * Copyright 2016-2019 NXP
 */
#include <linux/init.h>
#include <linux/module.h>
@@ -1276,6 +1276,12 @@ static int link_state_update(struct dpaa2_eth_priv *priv)
		   !!(state.options & DPNI_LINK_OPT_ASYM_PAUSE);
	dpaa2_eth_set_rx_taildrop(priv, !tx_pause);

	/* When we manage the MAC/PHY using phylink there is no need
	 * to manually update the netif_carrier.
	 */
	if (priv->mac)
		goto out;

	/* Chech link state; speed / duplex changes are not treated yet */
	if (priv->link_state.up == state.up)
		goto out;
@@ -1312,17 +1318,21 @@ static int dpaa2_eth_open(struct net_device *net_dev)
			   priv->dpbp_dev->obj_desc.id, priv->bpid);
	}

	/* We'll only start the txqs when the link is actually ready; make sure
	 * we don't race against the link up notification, which may come
	 * immediately after dpni_enable();
	if (!priv->mac) {
		/* We'll only start the txqs when the link is actually ready;
		 * make sure we don't race against the link up notification,
		 * which may come immediately after dpni_enable();
		 */
		netif_tx_stop_all_queues(net_dev);
	enable_ch_napi(priv);
	/* Also, explicitly set carrier off, otherwise netif_carrier_ok() will
	 * return true and cause 'ip link show' to report the LOWER_UP flag,
	 * even though the link notification wasn't even received.

		/* Also, explicitly set carrier off, otherwise
		 * netif_carrier_ok() will return true and cause 'ip link show'
		 * to report the LOWER_UP flag, even though the link
		 * notification wasn't even received.
		 */
		netif_carrier_off(net_dev);
	}
	enable_ch_napi(priv);

	err = dpni_enable(priv->mc_io, 0, priv->mc_token);
	if (err < 0) {
@@ -1330,14 +1340,18 @@ static int dpaa2_eth_open(struct net_device *net_dev)
		goto enable_err;
	}

	/* If the DPMAC object has already processed the link up interrupt,
	 * we have to learn the link state ourselves.
	if (!priv->mac) {
		/* If the DPMAC object has already processed the link up
		 * interrupt, we have to learn the link state ourselves.
		 */
		err = link_state_update(priv);
		if (err < 0) {
			netdev_err(net_dev, "Can't update link state\n");
			goto link_state_err;
		}
	} else {
		phylink_start(priv->mac->phylink);
	}

	return 0;

@@ -1411,8 +1425,12 @@ static int dpaa2_eth_stop(struct net_device *net_dev)
	int dpni_enabled = 0;
	int retries = 10;

	if (!priv->mac) {
		netif_tx_stop_all_queues(net_dev);
		netif_carrier_off(net_dev);
	} else {
		phylink_stop(priv->mac->phylink);
	}

	/* On dpni_disable(), the MC firmware will:
	 * - stop MAC Rx and wait for all Rx frames to be enqueued to software
@@ -3342,12 +3360,56 @@ static int poll_link_state(void *arg)
	return 0;
}

static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
{
	struct fsl_mc_device *dpni_dev, *dpmac_dev;
	struct dpaa2_mac *mac;
	int err;

	dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent);
	dpmac_dev = fsl_mc_get_endpoint(dpni_dev);
	if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type)
		return 0;

	if (dpaa2_mac_is_type_fixed(dpmac_dev, priv->mc_io))
		return 0;

	mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL);
	if (!mac)
		return -ENOMEM;

	mac->mc_dev = dpmac_dev;
	mac->mc_io = priv->mc_io;
	mac->net_dev = priv->net_dev;

	err = dpaa2_mac_connect(mac);
	if (err) {
		netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n");
		kfree(mac);
		return err;
	}
	priv->mac = mac;

	return 0;
}

static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
{
	if (!priv->mac)
		return;

	dpaa2_mac_disconnect(priv->mac);
	kfree(priv->mac);
	priv->mac = NULL;
}

static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
{
	u32 status = ~0;
	struct device *dev = (struct device *)arg;
	struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
	struct net_device *net_dev = dev_get_drvdata(dev);
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
	int err;

	err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
@@ -3363,6 +3425,13 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
	if (status & DPNI_IRQ_EVENT_ENDPOINT_CHANGED) {
		set_mac_addr(netdev_priv(net_dev));
		update_tx_fqids(priv);

		rtnl_lock();
		if (priv->mac)
			dpaa2_eth_disconnect_mac(priv);
		else
			dpaa2_eth_connect_mac(priv);
		rtnl_unlock();
	}

	return IRQ_HANDLED;
@@ -3539,6 +3608,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
		priv->do_link_poll = true;
	}

	err = dpaa2_eth_connect_mac(priv);
	if (err)
		goto err_connect_mac;

	err = register_netdev(net_dev);
	if (err < 0) {
		dev_err(dev, "register_netdev() failed\n");
@@ -3553,6 +3626,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
	return 0;

err_netdev_reg:
	dpaa2_eth_disconnect_mac(priv);
err_connect_mac:
	if (priv->do_link_poll)
		kthread_stop(priv->poll_thread);
	else
@@ -3595,6 +3670,10 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
#ifdef CONFIG_DEBUG_FS
	dpaa2_dbg_remove(priv);
#endif
	rtnl_lock();
	dpaa2_eth_disconnect_mac(priv);
	rtnl_unlock();

	unregister_netdev(net_dev);

	if (priv->do_link_poll)
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

#include "dpaa2-eth-trace.h"
#include "dpaa2-eth-debugfs.h"
#include "dpaa2-mac.h"

#define DPAA2_WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0)

@@ -415,6 +416,8 @@ struct dpaa2_eth_priv {
#ifdef CONFIG_DEBUG_FS
	struct dpaa2_debugfs dbg;
#endif

	struct dpaa2_mac *mac;
};

#define DPAA2_RXH_SUPPORTED	(RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \
+25 −0
Original line number Diff line number Diff line
@@ -85,6 +85,10 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev,
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	if (priv->mac)
		return phylink_ethtool_ksettings_get(priv->mac->phylink,
						     link_settings);

	link_settings->base.autoneg = AUTONEG_DISABLE;
	if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX))
		link_settings->base.duplex = DUPLEX_FULL;
@@ -93,12 +97,29 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev,
	return 0;
}

static int
dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
			     const struct ethtool_link_ksettings *link_settings)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);

	if (!priv->mac)
		return -ENOTSUPP;

	return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
}

static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
				     struct ethtool_pauseparam *pause)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
	u64 link_options = priv->link_state.options;

	if (priv->mac) {
		phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
		return;
	}

	pause->rx_pause = !!(link_options & DPNI_LINK_OPT_PAUSE);
	pause->tx_pause = pause->rx_pause ^
			  !!(link_options & DPNI_LINK_OPT_ASYM_PAUSE);
@@ -118,6 +139,9 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev,
		return -EOPNOTSUPP;
	}

	if (priv->mac)
		return phylink_ethtool_set_pauseparam(priv->mac->phylink,
						      pause);
	if (pause->autoneg)
		return -EOPNOTSUPP;

@@ -728,6 +752,7 @@ const struct ethtool_ops dpaa2_ethtool_ops = {
	.get_drvinfo = dpaa2_eth_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_link_ksettings = dpaa2_eth_get_link_ksettings,
	.set_link_ksettings = dpaa2_eth_set_link_ksettings,
	.get_pauseparam = dpaa2_eth_get_pauseparam,
	.set_pauseparam = dpaa2_eth_set_pauseparam,
	.get_sset_count = dpaa2_eth_get_sset_count,
Loading